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
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.
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, see <http://www.gnu.org/licenses/>.
23 #include "system/filesys.h"
24 #include "system/time.h"
26 #include "smbprofile.h"
27 #include "lib/tdb_wrap/tdb_wrap.h"
29 #include "../lib/crypto/crypto.h"
31 #ifdef HAVE_SYS_RESOURCE_H
32 #include <sys/resource.h>
35 #include <gnutls/gnutls.h>
36 #include <gnutls/crypto.h>
37 #include "lib/crypto/gnutls_helpers.h"
39 struct profile_stats
*profile_p
;
40 struct smbprofile_global_state smbprofile_state
;
42 /****************************************************************************
43 Set a profiling level.
44 ****************************************************************************/
45 void set_profile_level(int level
, const struct server_id
*src
)
47 SMB_ASSERT(smbprofile_state
.internal
.db
!= NULL
);
50 case 0: /* turn off profiling */
51 smbprofile_state
.config
.do_count
= false;
52 smbprofile_state
.config
.do_times
= false;
53 DEBUG(1,("INFO: Profiling turned OFF from pid %d\n",
54 (int)procid_to_pid(src
)));
56 case 1: /* turn on counter profiling only */
57 smbprofile_state
.config
.do_count
= true;
58 smbprofile_state
.config
.do_times
= false;
59 DEBUG(1,("INFO: Profiling counts turned ON from pid %d\n",
60 (int)procid_to_pid(src
)));
62 case 2: /* turn on complete profiling */
63 smbprofile_state
.config
.do_count
= true;
64 smbprofile_state
.config
.do_times
= true;
65 DEBUG(1,("INFO: Full profiling turned ON from pid %d\n",
66 (int)procid_to_pid(src
)));
68 case 3: /* reset profile values */
69 ZERO_STRUCT(profile_p
->values
);
70 tdb_wipe_all(smbprofile_state
.internal
.db
->tdb
);
71 DEBUG(1,("INFO: Profiling values cleared from pid %d\n",
72 (int)procid_to_pid(src
)));
77 /****************************************************************************
78 receive a set profile level message
79 ****************************************************************************/
80 static void profile_message(struct messaging_context
*msg_ctx
,
88 if (data
->length
!= sizeof(level
)) {
89 DEBUG(0, ("got invalid profile message\n"));
93 memcpy(&level
, data
->data
, sizeof(level
));
94 set_profile_level(level
, &src
);
97 /****************************************************************************
98 receive a request profile level message
99 ****************************************************************************/
100 static void reqprofile_message(struct messaging_context
*msg_ctx
,
103 struct server_id src
,
109 if (smbprofile_state
.config
.do_count
) {
112 if (smbprofile_state
.config
.do_times
) {
116 DEBUG(1,("INFO: Received REQ_PROFILELEVEL message from PID %u\n",
117 (unsigned int)procid_to_pid(&src
)));
118 messaging_send_buf(msg_ctx
, src
, MSG_PROFILELEVEL
,
119 (uint8_t *)&level
, sizeof(level
));
122 /*******************************************************************
123 open the profiling shared memory area
124 ******************************************************************/
125 bool profile_setup(struct messaging_context
*msg_ctx
, bool rdonly
)
127 uint8_t digest
[gnutls_hash_get_len(GNUTLS_DIG_SHA1
)];
128 gnutls_hash_hd_t hash_hnd
= NULL
;
133 if (smbprofile_state
.internal
.db
!= NULL
) {
137 db_name
= cache_path(talloc_tos(), "smbprofile.tdb");
138 if (db_name
== NULL
) {
142 smbprofile_state
.internal
.db
= tdb_wrap_open(
144 rdonly
? 0 : TDB_CLEAR_IF_FIRST
|TDB_MUTEX_LOCKING
,
145 O_CREAT
| (rdonly
? O_RDONLY
: O_RDWR
), 0644);
146 if (smbprofile_state
.internal
.db
== NULL
) {
150 if (msg_ctx
!= NULL
) {
151 messaging_register(msg_ctx
, NULL
, MSG_PROFILE
,
153 messaging_register(msg_ctx
, NULL
, MSG_REQ_PROFILELEVEL
,
157 GNUTLS_FIPS140_SET_LAX_MODE();
159 rc
= gnutls_hash_init(&hash_hnd
, GNUTLS_DIG_SHA1
);
163 rc
= gnutls_hash(hash_hnd
,
164 &smbprofile_state
.stats
.global
,
165 sizeof(smbprofile_state
.stats
.global
));
167 #define __UPDATE(str) do { \
168 rc |= gnutls_hash(hash_hnd, str, strlen(str)); \
170 #define SMBPROFILE_STATS_START
171 #define SMBPROFILE_STATS_SECTION_START(name, display) do { \
172 __UPDATE(#name "+" #display); \
174 #define SMBPROFILE_STATS_COUNT(name) do { \
175 __UPDATE(#name "+count"); \
177 #define SMBPROFILE_STATS_TIME(name) do { \
178 __UPDATE(#name "+time"); \
180 #define SMBPROFILE_STATS_BASIC(name) do { \
181 __UPDATE(#name "+count"); \
182 __UPDATE(#name "+time"); \
184 #define SMBPROFILE_STATS_BYTES(name) do { \
185 __UPDATE(#name "+count"); \
186 __UPDATE(#name "+time"); \
187 __UPDATE(#name "+idle"); \
188 __UPDATE(#name "+bytes"); \
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"); \
197 #define SMBPROFILE_STATS_SECTION_END
198 #define SMBPROFILE_STATS_END
199 SMBPROFILE_STATS_ALL_SECTIONS
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
211 gnutls_hash_deinit(hash_hnd
, NULL
);
215 gnutls_hash_deinit(hash_hnd
, digest
);
217 GNUTLS_FIPS140_SET_STRICT_MODE();
219 profile_p
= &smbprofile_state
.stats
.global
;
221 profile_p
->magic
= BVAL(digest
, 0);
222 if (profile_p
->magic
== 0) {
223 profile_p
->magic
= BVAL(digest
, 8);
228 GNUTLS_FIPS140_SET_STRICT_MODE();
233 void smbprofile_dump_setup(struct tevent_context
*ev
)
235 TALLOC_FREE(smbprofile_state
.internal
.te
);
236 smbprofile_state
.internal
.ev
= ev
;
239 static void smbprofile_dump_timer(struct tevent_context
*ev
,
240 struct tevent_timer
*te
,
241 struct timeval current_time
,
247 void smbprofile_dump_schedule_timer(void)
254 smbprofile_state
.internal
.te
= tevent_add_timer(
255 smbprofile_state
.internal
.ev
,
256 smbprofile_state
.internal
.ev
,
258 smbprofile_dump_timer
,
262 static int profile_stats_parser(TDB_DATA key
, TDB_DATA value
,
265 struct profile_stats
*s
= private_data
;
267 if (value
.dsize
!= sizeof(struct profile_stats
)) {
268 *s
= (struct profile_stats
) {};
272 memcpy(s
, value
.dptr
, value
.dsize
);
273 if (s
->magic
!= profile_p
->magic
) {
274 *s
= (struct profile_stats
) {};
281 void smbprofile_dump(void)
283 pid_t pid
= getpid();
284 TDB_DATA key
= { .dptr
= (uint8_t *)&pid
, .dsize
= sizeof(pid
) };
285 struct profile_stats s
= {};
287 #ifdef HAVE_GETRUSAGE
289 #endif /* HAVE_GETRUSAGE */
291 TALLOC_FREE(smbprofile_state
.internal
.te
);
293 if (! (smbprofile_state
.config
.do_count
||
294 smbprofile_state
.config
.do_times
)) {
298 if (smbprofile_state
.internal
.db
== NULL
) {
302 #ifdef HAVE_GETRUSAGE
303 ret
= getrusage(RUSAGE_SELF
, &rself
);
308 profile_p
->values
.cpu_user_stats
.time
=
309 (rself
.ru_utime
.tv_sec
* 1000000) +
310 rself
.ru_utime
.tv_usec
;
311 profile_p
->values
.cpu_system_stats
.time
=
312 (rself
.ru_stime
.tv_sec
* 1000000) +
313 rself
.ru_stime
.tv_usec
;
314 #endif /* HAVE_GETRUSAGE */
316 ret
= tdb_chainlock(smbprofile_state
.internal
.db
->tdb
, key
);
321 tdb_parse_record(smbprofile_state
.internal
.db
->tdb
,
322 key
, profile_stats_parser
, &s
);
324 smbprofile_stats_accumulate(profile_p
, &s
);
326 tdb_store(smbprofile_state
.internal
.db
->tdb
, key
,
328 .dptr
= (uint8_t *)profile_p
,
329 .dsize
= sizeof(*profile_p
)
333 tdb_chainunlock(smbprofile_state
.internal
.db
->tdb
, key
);
334 ZERO_STRUCT(profile_p
->values
);
339 void smbprofile_cleanup(pid_t pid
, pid_t dst
)
341 TDB_DATA key
= { .dptr
= (uint8_t *)&pid
, .dsize
= sizeof(pid
) };
342 struct profile_stats s
= {};
343 struct profile_stats acc
= {};
346 if (smbprofile_state
.internal
.db
== NULL
) {
350 ret
= tdb_chainlock(smbprofile_state
.internal
.db
->tdb
, key
);
354 ret
= tdb_parse_record(smbprofile_state
.internal
.db
->tdb
,
355 key
, profile_stats_parser
, &s
);
357 tdb_chainunlock(smbprofile_state
.internal
.db
->tdb
, key
);
360 tdb_delete(smbprofile_state
.internal
.db
->tdb
, key
);
361 tdb_chainunlock(smbprofile_state
.internal
.db
->tdb
, key
);
364 ret
= tdb_chainlock(smbprofile_state
.internal
.db
->tdb
, key
);
368 tdb_parse_record(smbprofile_state
.internal
.db
->tdb
,
369 key
, profile_stats_parser
, &acc
);
372 * We may have to fix the disconnect count
373 * in case the process died
375 s
.values
.disconnect_stats
.count
= s
.values
.connect_stats
.count
;
377 smbprofile_stats_accumulate(&acc
, &s
);
379 acc
.magic
= profile_p
->magic
;
380 tdb_store(smbprofile_state
.internal
.db
->tdb
, key
,
382 .dptr
= (uint8_t *)&acc
,
387 tdb_chainunlock(smbprofile_state
.internal
.db
->tdb
, key
);
390 void smbprofile_stats_accumulate(struct profile_stats
*acc
,
391 const struct profile_stats
*add
)
393 #define SMBPROFILE_STATS_START
394 #define SMBPROFILE_STATS_SECTION_START(name, display)
395 #define SMBPROFILE_STATS_COUNT(name) do { \
396 acc->values.name##_stats.count += add->values.name##_stats.count; \
398 #define SMBPROFILE_STATS_TIME(name) do { \
399 acc->values.name##_stats.time += add->values.name##_stats.time; \
401 #define SMBPROFILE_STATS_BASIC(name) do { \
402 acc->values.name##_stats.count += add->values.name##_stats.count; \
403 acc->values.name##_stats.time += add->values.name##_stats.time; \
405 #define SMBPROFILE_STATS_BYTES(name) do { \
406 acc->values.name##_stats.count += add->values.name##_stats.count; \
407 acc->values.name##_stats.time += add->values.name##_stats.time; \
408 acc->values.name##_stats.idle += add->values.name##_stats.idle; \
409 acc->values.name##_stats.bytes += add->values.name##_stats.bytes; \
411 #define SMBPROFILE_STATS_IOBYTES(name) do { \
412 acc->values.name##_stats.count += add->values.name##_stats.count; \
413 acc->values.name##_stats.time += add->values.name##_stats.time; \
414 acc->values.name##_stats.idle += add->values.name##_stats.idle; \
415 acc->values.name##_stats.inbytes += add->values.name##_stats.inbytes; \
416 acc->values.name##_stats.outbytes += add->values.name##_stats.outbytes; \
418 #define SMBPROFILE_STATS_SECTION_END
419 #define SMBPROFILE_STATS_END
420 SMBPROFILE_STATS_ALL_SECTIONS
421 #undef SMBPROFILE_STATS_START
422 #undef SMBPROFILE_STATS_SECTION_START
423 #undef SMBPROFILE_STATS_COUNT
424 #undef SMBPROFILE_STATS_TIME
425 #undef SMBPROFILE_STATS_BASIC
426 #undef SMBPROFILE_STATS_BYTES
427 #undef SMBPROFILE_STATS_IOBYTES
428 #undef SMBPROFILE_STATS_SECTION_END
429 #undef SMBPROFILE_STATS_END
432 static int smbprofile_collect_fn(struct tdb_context
*tdb
,
433 TDB_DATA key
, TDB_DATA value
,
436 struct profile_stats
*acc
= (struct profile_stats
*)private_data
;
437 const struct profile_stats
*v
;
439 if (value
.dsize
!= sizeof(struct profile_stats
)) {
443 v
= (const struct profile_stats
*)value
.dptr
;
445 if (v
->magic
!= profile_p
->magic
) {
449 smbprofile_stats_accumulate(acc
, v
);
453 void smbprofile_collect(struct profile_stats
*stats
)
455 *stats
= (struct profile_stats
) {};
457 if (smbprofile_state
.internal
.db
== NULL
) {
461 tdb_traverse_read(smbprofile_state
.internal
.db
->tdb
,
462 smbprofile_collect_fn
, stats
);