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