smbprofile: Add dst pid to smbprofile_cleanup
[obnox/samba/samba-obnox.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    Copyright (C) James Peach 2006
6
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 3 of the License, or
10    (at your option) any later version.
11
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.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 */
21
22 #include "includes.h"
23 #include "system/shmem.h"
24 #include "system/filesys.h"
25 #include "system/time.h"
26 #include "messages.h"
27 #include "smbprofile.h"
28 #include "lib/tdb_wrap/tdb_wrap.h"
29 #include <tevent.h>
30 #include "../lib/crypto/crypto.h"
31
32 #ifdef HAVE_SYS_RESOURCE_H
33 #include <sys/resource.h>
34 #endif
35
36 struct profile_stats *profile_p;
37 struct smbprofile_global_state smbprofile_state;
38
39 /****************************************************************************
40 Set a profiling level.
41 ****************************************************************************/
42 void set_profile_level(int level, struct server_id src)
43 {
44         SMB_ASSERT(smbprofile_state.internal.db != NULL);
45
46         switch (level) {
47         case 0:         /* turn off profiling */
48                 smbprofile_state.config.do_count = false;
49                 smbprofile_state.config.do_times = false;
50                 DEBUG(1,("INFO: Profiling turned OFF from pid %d\n",
51                          (int)procid_to_pid(&src)));
52                 break;
53         case 1:         /* turn on counter profiling only */
54                 smbprofile_state.config.do_count = true;
55                 smbprofile_state.config.do_times = false;
56                 DEBUG(1,("INFO: Profiling counts turned ON from pid %d\n",
57                          (int)procid_to_pid(&src)));
58                 break;
59         case 2:         /* turn on complete profiling */
60                 smbprofile_state.config.do_count = true;
61                 smbprofile_state.config.do_times = true;
62                 DEBUG(1,("INFO: Full profiling turned ON from pid %d\n",
63                          (int)procid_to_pid(&src)));
64                 break;
65         case 3:         /* reset profile values */
66                 ZERO_STRUCT(profile_p->values);
67                 tdb_wipe_all(smbprofile_state.internal.db->tdb);
68                 DEBUG(1,("INFO: Profiling values cleared from pid %d\n",
69                          (int)procid_to_pid(&src)));
70                 break;
71         }
72 }
73
74 /****************************************************************************
75 receive a set profile level message
76 ****************************************************************************/
77 static void profile_message(struct messaging_context *msg_ctx,
78                             void *private_data,
79                             uint32_t msg_type,
80                             struct server_id src,
81                             DATA_BLOB *data)
82 {
83         int level;
84
85         if (data->length != sizeof(level)) {
86                 DEBUG(0, ("got invalid profile message\n"));
87                 return;
88         }
89
90         memcpy(&level, data->data, sizeof(level));
91         set_profile_level(level, src);
92 }
93
94 /****************************************************************************
95 receive a request profile level message
96 ****************************************************************************/
97 static void reqprofile_message(struct messaging_context *msg_ctx,
98                                void *private_data,
99                                uint32_t msg_type,
100                                struct server_id src,
101                                DATA_BLOB *data)
102 {
103         int level;
104
105         level = 1;
106         if (smbprofile_state.config.do_count) {
107                 level += 2;
108         }
109         if (smbprofile_state.config.do_times) {
110                 level += 4;
111         }
112
113         DEBUG(1,("INFO: Received REQ_PROFILELEVEL message from PID %u\n",
114                  (unsigned int)procid_to_pid(&src)));
115         messaging_send_buf(msg_ctx, src, MSG_PROFILELEVEL,
116                            (uint8_t *)&level, sizeof(level));
117 }
118
119 /*******************************************************************
120   open the profiling shared memory area
121   ******************************************************************/
122 bool profile_setup(struct messaging_context *msg_ctx, bool rdonly)
123 {
124         unsigned char tmp[16] = {};
125         MD5_CTX md5;
126         char *db_name;
127
128         if (smbprofile_state.internal.db != NULL) {
129                 return true;
130         }
131
132         db_name = cache_path("smbprofile.tdb");
133         if (db_name == NULL) {
134                 return false;
135         }
136
137         smbprofile_state.internal.db = tdb_wrap_open(
138                 NULL, db_name, 0,
139                 rdonly ? 0 : TDB_CLEAR_IF_FIRST|TDB_MUTEX_LOCKING,
140                 O_CREAT | (rdonly ? O_RDONLY : O_RDWR), 0644);
141         if (smbprofile_state.internal.db == NULL) {
142                 return false;
143         }
144
145         if (msg_ctx != NULL) {
146                 messaging_register(msg_ctx, NULL, MSG_PROFILE,
147                                    profile_message);
148                 messaging_register(msg_ctx, NULL, MSG_REQ_PROFILELEVEL,
149                                    reqprofile_message);
150         }
151
152         MD5Init(&md5);
153
154         MD5Update(&md5,
155                   (const uint8_t *)&smbprofile_state.stats.global,
156                   sizeof(smbprofile_state.stats.global));
157
158 #define __UPDATE(str) do { \
159         MD5Update(&md5, (const uint8_t *)str, strlen(str)); \
160 } while(0)
161 #define SMBPROFILE_STATS_START
162 #define SMBPROFILE_STATS_SECTION_START(name, display) do { \
163         __UPDATE(#name "+" #display); \
164 } while(0);
165 #define SMBPROFILE_STATS_COUNT(name) do { \
166         __UPDATE(#name "+count"); \
167 } while(0);
168 #define SMBPROFILE_STATS_TIME(name) do { \
169         __UPDATE(#name "+time"); \
170 } while(0);
171 #define SMBPROFILE_STATS_BASIC(name) do { \
172         __UPDATE(#name "+count"); \
173         __UPDATE(#name "+time"); \
174 } while(0);
175 #define SMBPROFILE_STATS_BYTES(name) do { \
176         __UPDATE(#name "+count"); \
177         __UPDATE(#name "+time"); \
178         __UPDATE(#name "+idle"); \
179         __UPDATE(#name "+bytes"); \
180 } while(0);
181 #define SMBPROFILE_STATS_IOBYTES(name) do { \
182         __UPDATE(#name "+count"); \
183         __UPDATE(#name "+time"); \
184         __UPDATE(#name "+idle"); \
185         __UPDATE(#name "+inbytes"); \
186         __UPDATE(#name "+outbytes"); \
187 } while(0);
188 #define SMBPROFILE_STATS_SECTION_END
189 #define SMBPROFILE_STATS_END
190         SMBPROFILE_STATS_ALL_SECTIONS
191 #undef __UPDATE
192 #undef SMBPROFILE_STATS_START
193 #undef SMBPROFILE_STATS_SECTION_START
194 #undef SMBPROFILE_STATS_COUNT
195 #undef SMBPROFILE_STATS_TIME
196 #undef SMBPROFILE_STATS_BASIC
197 #undef SMBPROFILE_STATS_BYTES
198 #undef SMBPROFILE_STATS_IOBYTES
199 #undef SMBPROFILE_STATS_SECTION_END
200 #undef SMBPROFILE_STATS_END
201
202         MD5Final(tmp, &md5);
203
204         profile_p = &smbprofile_state.stats.global;
205
206         profile_p->magic = BVAL(tmp, 0);
207         if (profile_p->magic == 0) {
208                 profile_p->magic = BVAL(tmp, 8);
209         }
210
211         return True;
212 }
213
214 void smbprofile_dump_setup(struct tevent_context *ev)
215 {
216         TALLOC_FREE(smbprofile_state.internal.te);
217         smbprofile_state.internal.ev = ev;
218 }
219
220 static void smbprofile_dump_timer(struct tevent_context *ev,
221                                   struct tevent_timer *te,
222                                   struct timeval current_time,
223                                   void *private_data)
224 {
225         smbprofile_dump();
226 }
227
228 void smbprofile_dump_schedule_timer(void)
229 {
230         struct timeval tv;
231
232         GetTimeOfDay(&tv);
233         tv.tv_sec += 1;
234
235         smbprofile_state.internal.te = tevent_add_timer(
236                                 smbprofile_state.internal.ev,
237                                 smbprofile_state.internal.ev,
238                                 tv,
239                                 smbprofile_dump_timer,
240                                 NULL);
241 }
242
243 static int profile_stats_parser(TDB_DATA key, TDB_DATA value,
244                                 void *private_data)
245 {
246         struct profile_stats *s = private_data;
247
248         if (value.dsize != sizeof(struct profile_stats)) {
249                 *s = (struct profile_stats) {};
250                 return 0;
251         }
252
253         memcpy(s, value.dptr, value.dsize);
254         if (s->magic != profile_p->magic) {
255                 *s = (struct profile_stats) {};
256                 return 0;
257         }
258
259         return 0;
260 }
261
262 void smbprofile_dump(void)
263 {
264         pid_t pid = getpid();
265         TDB_DATA key = { .dptr = (uint8_t *)&pid, .dsize = sizeof(pid) };
266         struct profile_stats s = {};
267         int ret;
268 #ifdef HAVE_GETRUSAGE
269         struct rusage rself;
270 #endif /* HAVE_GETRUSAGE */
271
272         TALLOC_FREE(smbprofile_state.internal.te);
273
274         if (smbprofile_state.internal.db == NULL) {
275                 return;
276         }
277
278 #ifdef HAVE_GETRUSAGE
279         ret = getrusage(RUSAGE_SELF, &rself);
280         if (ret != 0) {
281                 ZERO_STRUCT(rself);
282         }
283
284         profile_p->values.cpu_user_stats.time =
285                 (rself.ru_utime.tv_sec * 1000000) +
286                 rself.ru_utime.tv_usec;
287         profile_p->values.cpu_system_stats.time =
288                 (rself.ru_stime.tv_sec * 1000000) +
289                 rself.ru_stime.tv_usec;
290 #endif /* HAVE_GETRUSAGE */
291
292         ret = tdb_chainlock(smbprofile_state.internal.db->tdb, key);
293         if (ret != 0) {
294                 return;
295         }
296
297         tdb_parse_record(smbprofile_state.internal.db->tdb,
298                          key, profile_stats_parser, &s);
299
300         smbprofile_stats_accumulate(profile_p, &s);
301
302         tdb_store(smbprofile_state.internal.db->tdb, key,
303                   (TDB_DATA) {
304                         .dptr = (uint8_t *)profile_p,
305                         .dsize = sizeof(*profile_p)
306                   },
307                   0);
308
309         tdb_chainunlock(smbprofile_state.internal.db->tdb, key);
310         ZERO_STRUCT(profile_p->values);
311
312         return;
313 }
314
315 void smbprofile_cleanup(pid_t pid, pid_t dst)
316 {
317         TDB_DATA key = { .dptr = (uint8_t *)&pid, .dsize = sizeof(pid) };
318         struct profile_stats s = {};
319         struct profile_stats acc = {};
320         int ret;
321
322         if (smbprofile_state.internal.db == NULL) {
323                 return;
324         }
325
326         ret = tdb_chainlock(smbprofile_state.internal.db->tdb, key);
327         if (ret != 0) {
328                 return;
329         }
330         ret = tdb_parse_record(smbprofile_state.internal.db->tdb,
331                                key, profile_stats_parser, &s);
332         if (ret == -1) {
333                 tdb_chainunlock(smbprofile_state.internal.db->tdb, key);
334                 return;
335         }
336         tdb_delete(smbprofile_state.internal.db->tdb, key);
337         tdb_chainunlock(smbprofile_state.internal.db->tdb, key);
338
339         pid = dst;
340         ret = tdb_chainlock(smbprofile_state.internal.db->tdb, key);
341         if (ret != 0) {
342                 return;
343         }
344         tdb_parse_record(smbprofile_state.internal.db->tdb,
345                          key, profile_stats_parser, &acc);
346
347         /*
348          * We may have to fix the disconnect count
349          * in case the process died
350          */
351         s.values.disconnect_stats.count = s.values.connect_stats.count;
352
353         smbprofile_stats_accumulate(&acc, &s);
354
355         acc.magic = profile_p->magic;
356         tdb_store(smbprofile_state.internal.db->tdb, key,
357                   (TDB_DATA) {
358                         .dptr = (uint8_t *)&acc,
359                         .dsize = sizeof(acc)
360                   },
361                   0);
362
363         tdb_chainunlock(smbprofile_state.internal.db->tdb, key);
364 }
365
366 void smbprofile_stats_accumulate(struct profile_stats *acc,
367                                  const struct profile_stats *add)
368 {
369 #define SMBPROFILE_STATS_START
370 #define SMBPROFILE_STATS_SECTION_START(name, display)
371 #define SMBPROFILE_STATS_COUNT(name) do { \
372         acc->values.name##_stats.count += add->values.name##_stats.count; \
373 } while(0);
374 #define SMBPROFILE_STATS_TIME(name) do { \
375         acc->values.name##_stats.time += add->values.name##_stats.time; \
376 } while(0);
377 #define SMBPROFILE_STATS_BASIC(name) do { \
378         acc->values.name##_stats.count += add->values.name##_stats.count; \
379         acc->values.name##_stats.time += add->values.name##_stats.time; \
380 } while(0);
381 #define SMBPROFILE_STATS_BYTES(name) do { \
382         acc->values.name##_stats.count += add->values.name##_stats.count; \
383         acc->values.name##_stats.time += add->values.name##_stats.time; \
384         acc->values.name##_stats.idle += add->values.name##_stats.idle; \
385         acc->values.name##_stats.bytes += add->values.name##_stats.bytes; \
386 } while(0);
387 #define SMBPROFILE_STATS_IOBYTES(name) do { \
388         acc->values.name##_stats.count += add->values.name##_stats.count; \
389         acc->values.name##_stats.time += add->values.name##_stats.time; \
390         acc->values.name##_stats.idle += add->values.name##_stats.idle; \
391         acc->values.name##_stats.inbytes += add->values.name##_stats.inbytes; \
392         acc->values.name##_stats.outbytes += add->values.name##_stats.outbytes; \
393 } while(0);
394 #define SMBPROFILE_STATS_SECTION_END
395 #define SMBPROFILE_STATS_END
396         SMBPROFILE_STATS_ALL_SECTIONS
397 #undef SMBPROFILE_STATS_START
398 #undef SMBPROFILE_STATS_SECTION_START
399 #undef SMBPROFILE_STATS_COUNT
400 #undef SMBPROFILE_STATS_TIME
401 #undef SMBPROFILE_STATS_BASIC
402 #undef SMBPROFILE_STATS_BYTES
403 #undef SMBPROFILE_STATS_IOBYTES
404 #undef SMBPROFILE_STATS_SECTION_END
405 #undef SMBPROFILE_STATS_END
406 }
407
408 static int smbprofile_collect_fn(struct tdb_context *tdb,
409                                  TDB_DATA key, TDB_DATA value,
410                                  void *private_data)
411 {
412         struct profile_stats *acc = (struct profile_stats *)private_data;
413         const struct profile_stats *v;
414
415         if (value.dsize != sizeof(struct profile_stats)) {
416                 return 0;
417         }
418
419         v = (const struct profile_stats *)value.dptr;
420
421         if (v->magic != profile_p->magic) {
422                 return 0;
423         }
424
425         smbprofile_stats_accumulate(acc, v);
426         return 0;
427 }
428
429 void smbprofile_collect(struct profile_stats *stats)
430 {
431         *stats = (struct profile_stats) {};
432
433         if (smbprofile_state.internal.db == NULL) {
434                 return;
435         }
436
437         tdb_traverse_read(smbprofile_state.internal.db->tdb,
438                           smbprofile_collect_fn, stats);
439 }