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