r16116: Hoist the slow CLOCK_REALTIME message inside the branch so we never
[kai/samba.git] / source3 / profile / profile.c
1 /* 
2    Unix SMB/CIFS implementation.
3    store smbd profiling information in shared memory
4    Copyright (C) Andrew Tridgell 1999
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 */
21
22 #include "includes.h"
23
24 #ifdef WITH_PROFILE
25 #define IPC_PERMS ((S_IRUSR | S_IWUSR) | S_IRGRP | S_IROTH)
26 #endif /* WITH_PROFILE */
27
28 #ifdef WITH_PROFILE
29 static int shm_id;
30 static BOOL read_only;
31 #if defined(HAVE_CLOCK_GETTIME)
32 clockid_t __profile_clock;
33 BOOL have_profiling_clock = False;
34 #endif
35 #endif
36
37 struct profile_header *profile_h;
38 struct profile_stats *profile_p;
39
40 BOOL do_profile_flag = False;
41 BOOL do_profile_times = False;
42
43 /****************************************************************************
44 receive a set profile level message
45 ****************************************************************************/
46 void profile_message(int msg_type, struct process_id src, void *buf, size_t len)
47 {
48         int level;
49
50         memcpy(&level, buf, sizeof(int));
51 #ifdef WITH_PROFILE
52         switch (level) {
53         case 0:         /* turn off profiling */
54                 do_profile_flag = False;
55                 do_profile_times = False;
56                 DEBUG(1,("INFO: Profiling turned OFF from pid %d\n",
57                          (int)procid_to_pid(&src)));
58                 break;
59         case 1:         /* turn on counter profiling only */
60                 do_profile_flag = True;
61                 do_profile_times = False;
62                 DEBUG(1,("INFO: Profiling counts turned ON from pid %d\n",
63                          (int)procid_to_pid(&src)));
64                 break;
65         case 2:         /* turn on complete profiling */
66
67 #if defined(HAVE_CLOCK_GETTIME)
68                 if (!have_profiling_clock) {
69                         do_profile_flag = True;
70                         do_profile_times = False;
71                         DEBUG(1,("INFO: Profiling counts turned ON from "
72                                 "pid %d\n", (int)procid_to_pid(&src)));
73                         DEBUGADD(1,("INFO: Profiling times disabled "
74                                 "due to lack of a suitable clock\n"));
75                         break;
76                 }
77 #endif
78
79                 do_profile_flag = True;
80                 do_profile_times = True;
81                 DEBUG(1,("INFO: Full profiling turned ON from pid %d\n",
82                          (int)procid_to_pid(&src)));
83                 break;
84         case 3:         /* reset profile values */
85                 memset((char *)profile_p, 0, sizeof(*profile_p));
86                 DEBUG(1,("INFO: Profiling values cleared from pid %d\n",
87                          (int)procid_to_pid(&src)));
88                 break;
89         }
90 #else /* WITH_PROFILE */
91         DEBUG(1,("INFO: Profiling support unavailable in this build.\n"));
92 #endif /* WITH_PROFILE */
93 }
94
95 /****************************************************************************
96 receive a request profile level message
97 ****************************************************************************/
98 void reqprofile_message(int msg_type, struct process_id src,
99                         void *buf, size_t len)
100 {
101         int level;
102
103 #ifdef WITH_PROFILE
104         level = 1 + (do_profile_flag?2:0) + (do_profile_times?4:0);
105 #else
106         level = 0;
107 #endif
108         DEBUG(1,("INFO: Received REQ_PROFILELEVEL message from PID %u\n",
109                  (unsigned int)procid_to_pid(&src)));
110         message_send_pid(src, MSG_PROFILELEVEL, &level, sizeof(int), True);
111 }
112
113 /*******************************************************************
114   open the profiling shared memory area
115   ******************************************************************/
116 #ifdef WITH_PROFILE
117
118 #ifdef HAVE_CLOCK_GETTIME
119
120 /* Find a clock. Just because the definition for a particular clock ID is
121  * present doesn't mean the system actually supports it.
122  */
123 static void init_clock_gettime(void)
124 {
125         struct timespec ts;
126
127         have_profiling_clock = False;
128
129 #ifdef HAVE_CLOCK_PROCESS_CPUTIME_ID
130         /* CLOCK_PROCESS_CPUTIME_ID is sufficiently fast that the
131          * always profiling times is plausible. Unfortunately on Linux
132          * it is only accurate if we can guarantee we will not be scheduled
133          * scheduled onto a different CPU between samples. Until there is
134          * some way to set processor affinity, we can only use this on
135          * uniprocessors.
136          */
137         if (!this_is_smp()) {
138             if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0) {
139                     DEBUG(10, ("Using CLOCK_PROCESS_CPUTIME_ID "
140                                 "for profile_clock\n"));
141                     __profile_clock = CLOCK_PROCESS_CPUTIME_ID;
142                     have_profiling_clock = True;
143             }
144         }
145 #endif
146
147 #ifdef HAVE_CLOCK_MONOTONIC
148         if (!have_profiling_clock &&
149             clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
150                 DEBUG(10, ("Using CLOCK_MONOTONIC for profile_clock\n"));
151                 __profile_clock = CLOCK_MONOTONIC;
152                 have_profiling_clock = True;
153         }
154 #endif
155
156 #ifdef HAVE_CLOCK_REALTIME
157         /* POSIX says that CLOCK_REALTIME should be defined everywhere
158          * where we have clock_gettime...
159          */
160         if (!have_profiling_clock &&
161             clock_gettime(CLOCK_REALTIME, &ts) == 0) {
162                 __profile_clock = CLOCK_REALTIME;
163                 have_profiling_clock = True;
164
165                 SMB_WARN(__profile_clock != CLOCK_REALTIME,
166                         ("forced to use a slow profiling clock"));
167         }
168
169 #endif
170
171         SMB_WARN(have_profiling_clock == True,
172                 ("could not find a working clock for profiling"));
173         return;
174 }
175 #endif
176
177 BOOL profile_setup(BOOL rdonly)
178 {
179         struct shmid_ds shm_ds;
180
181         read_only = rdonly;
182
183 #ifdef HAVE_CLOCK_GETTIME
184         init_clock_gettime();
185 #endif
186
187  again:
188         /* try to use an existing key */
189         shm_id = shmget(PROF_SHMEM_KEY, 0, 0);
190         
191         /* if that failed then create one. There is a race condition here
192            if we are running from inetd. Bad luck. */
193         if (shm_id == -1) {
194                 if (read_only) return False;
195                 shm_id = shmget(PROF_SHMEM_KEY, sizeof(*profile_h), 
196                                 IPC_CREAT | IPC_EXCL | IPC_PERMS);
197         }
198         
199         if (shm_id == -1) {
200                 DEBUG(0,("Can't create or use IPC area. Error was %s\n", 
201                          strerror(errno)));
202                 return False;
203         }   
204         
205         
206         profile_h = (struct profile_header *)shmat(shm_id, 0, 
207                                                    read_only?SHM_RDONLY:0);
208         if ((long)profile_p == -1) {
209                 DEBUG(0,("Can't attach to IPC area. Error was %s\n", 
210                          strerror(errno)));
211                 return False;
212         }
213
214         /* find out who created this memory area */
215         if (shmctl(shm_id, IPC_STAT, &shm_ds) != 0) {
216                 DEBUG(0,("ERROR shmctl : can't IPC_STAT. Error was %s\n", 
217                          strerror(errno)));
218                 return False;
219         }
220
221         if (shm_ds.shm_perm.cuid != sec_initial_uid() ||
222             shm_ds.shm_perm.cgid != sec_initial_gid()) {
223                 DEBUG(0,("ERROR: we did not create the shmem "
224                          "(owned by another user, uid %u, gid %u)\n",
225                          shm_ds.shm_perm.cuid,
226                          shm_ds.shm_perm.cgid));
227                 return False;
228         }
229
230         if (shm_ds.shm_segsz != sizeof(*profile_h)) {
231                 DEBUG(0,("WARNING: profile size is %d (expected %d). Deleting\n",
232                          (int)shm_ds.shm_segsz, sizeof(*profile_h)));
233                 if (shmctl(shm_id, IPC_RMID, &shm_ds) == 0) {
234                         goto again;
235                 } else {
236                         return False;
237                 }
238         }
239
240         if (!read_only && (shm_ds.shm_nattch == 1)) {
241                 memset((char *)profile_h, 0, sizeof(*profile_h));
242                 profile_h->prof_shm_magic = PROF_SHM_MAGIC;
243                 profile_h->prof_shm_version = PROF_SHM_VERSION;
244                 DEBUG(3,("Initialised profile area\n"));
245         }
246
247         profile_p = &profile_h->stats;
248         message_register(MSG_PROFILE, profile_message);
249         message_register(MSG_REQ_PROFILELEVEL, reqprofile_message);
250         return True;
251 }
252 #endif /* WITH_PROFILE */