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/shmem.h"
24 #include "system/filesys.h"
25 #include "system/time.h"
27 #include "smbprofile.h"
28 #include "lib/tdb_wrap/tdb_wrap.h"
30 #include "../lib/crypto/crypto.h"
32 #ifdef HAVE_SYS_RESOURCE_H
33 #include <sys/resource.h>
36 struct profile_stats
*profile_p
;
37 struct smbprofile_global_state smbprofile_state
;
39 /****************************************************************************
40 Set a profiling level.
41 ****************************************************************************/
42 void set_profile_level(int level
, const struct server_id
*src
)
44 SMB_ASSERT(smbprofile_state
.internal
.db
!= NULL
);
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
)));
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
)));
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
)));
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
)));
74 /****************************************************************************
75 receive a set profile level message
76 ****************************************************************************/
77 static void profile_message(struct messaging_context
*msg_ctx
,
85 if (data
->length
!= sizeof(level
)) {
86 DEBUG(0, ("got invalid profile message\n"));
90 memcpy(&level
, data
->data
, sizeof(level
));
91 set_profile_level(level
, &src
);
94 /****************************************************************************
95 receive a request profile level message
96 ****************************************************************************/
97 static void reqprofile_message(struct messaging_context
*msg_ctx
,
100 struct server_id src
,
106 if (smbprofile_state
.config
.do_count
) {
109 if (smbprofile_state
.config
.do_times
) {
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
));
119 /*******************************************************************
120 open the profiling shared memory area
121 ******************************************************************/
122 bool profile_setup(struct messaging_context
*msg_ctx
, bool rdonly
)
124 unsigned char tmp
[16] = {};
128 if (smbprofile_state
.internal
.db
!= NULL
) {
132 db_name
= cache_path(talloc_tos(), "smbprofile.tdb");
133 if (db_name
== NULL
) {
137 smbprofile_state
.internal
.db
= tdb_wrap_open(
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
) {
145 if (msg_ctx
!= NULL
) {
146 messaging_register(msg_ctx
, NULL
, MSG_PROFILE
,
148 messaging_register(msg_ctx
, NULL
, MSG_REQ_PROFILELEVEL
,
155 (const uint8_t *)&smbprofile_state
.stats
.global
,
156 sizeof(smbprofile_state
.stats
.global
));
158 #define __UPDATE(str) do { \
159 MD5Update(&md5, (const uint8_t *)str, strlen(str)); \
161 #define SMBPROFILE_STATS_START
162 #define SMBPROFILE_STATS_SECTION_START(name, display) do { \
163 __UPDATE(#name "+" #display); \
165 #define SMBPROFILE_STATS_COUNT(name) do { \
166 __UPDATE(#name "+count"); \
168 #define SMBPROFILE_STATS_TIME(name) do { \
169 __UPDATE(#name "+time"); \
171 #define SMBPROFILE_STATS_BASIC(name) do { \
172 __UPDATE(#name "+count"); \
173 __UPDATE(#name "+time"); \
175 #define SMBPROFILE_STATS_BYTES(name) do { \
176 __UPDATE(#name "+count"); \
177 __UPDATE(#name "+time"); \
178 __UPDATE(#name "+idle"); \
179 __UPDATE(#name "+bytes"); \
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"); \
188 #define SMBPROFILE_STATS_SECTION_END
189 #define SMBPROFILE_STATS_END
190 SMBPROFILE_STATS_ALL_SECTIONS
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
204 profile_p
= &smbprofile_state
.stats
.global
;
206 profile_p
->magic
= BVAL(tmp
, 0);
207 if (profile_p
->magic
== 0) {
208 profile_p
->magic
= BVAL(tmp
, 8);
214 void smbprofile_dump_setup(struct tevent_context
*ev
)
216 TALLOC_FREE(smbprofile_state
.internal
.te
);
217 smbprofile_state
.internal
.ev
= ev
;
220 static void smbprofile_dump_timer(struct tevent_context
*ev
,
221 struct tevent_timer
*te
,
222 struct timeval current_time
,
228 void smbprofile_dump_schedule_timer(void)
235 smbprofile_state
.internal
.te
= tevent_add_timer(
236 smbprofile_state
.internal
.ev
,
237 smbprofile_state
.internal
.ev
,
239 smbprofile_dump_timer
,
243 static int profile_stats_parser(TDB_DATA key
, TDB_DATA value
,
246 struct profile_stats
*s
= private_data
;
248 if (value
.dsize
!= sizeof(struct profile_stats
)) {
249 *s
= (struct profile_stats
) {};
253 memcpy(s
, value
.dptr
, value
.dsize
);
254 if (s
->magic
!= profile_p
->magic
) {
255 *s
= (struct profile_stats
) {};
262 void smbprofile_dump(void)
264 pid_t pid
= getpid();
265 TDB_DATA key
= { .dptr
= (uint8_t *)&pid
, .dsize
= sizeof(pid
) };
266 struct profile_stats s
= {};
268 #ifdef HAVE_GETRUSAGE
270 #endif /* HAVE_GETRUSAGE */
272 TALLOC_FREE(smbprofile_state
.internal
.te
);
274 if (! (smbprofile_state
.config
.do_count
||
275 smbprofile_state
.config
.do_times
)) {
279 if (smbprofile_state
.internal
.db
== NULL
) {
283 #ifdef HAVE_GETRUSAGE
284 ret
= getrusage(RUSAGE_SELF
, &rself
);
289 profile_p
->values
.cpu_user_stats
.time
=
290 (rself
.ru_utime
.tv_sec
* 1000000) +
291 rself
.ru_utime
.tv_usec
;
292 profile_p
->values
.cpu_system_stats
.time
=
293 (rself
.ru_stime
.tv_sec
* 1000000) +
294 rself
.ru_stime
.tv_usec
;
295 #endif /* HAVE_GETRUSAGE */
297 ret
= tdb_chainlock(smbprofile_state
.internal
.db
->tdb
, key
);
302 tdb_parse_record(smbprofile_state
.internal
.db
->tdb
,
303 key
, profile_stats_parser
, &s
);
305 smbprofile_stats_accumulate(profile_p
, &s
);
307 tdb_store(smbprofile_state
.internal
.db
->tdb
, key
,
309 .dptr
= (uint8_t *)profile_p
,
310 .dsize
= sizeof(*profile_p
)
314 tdb_chainunlock(smbprofile_state
.internal
.db
->tdb
, key
);
315 ZERO_STRUCT(profile_p
->values
);
320 void smbprofile_cleanup(pid_t pid
, pid_t dst
)
322 TDB_DATA key
= { .dptr
= (uint8_t *)&pid
, .dsize
= sizeof(pid
) };
323 struct profile_stats s
= {};
324 struct profile_stats acc
= {};
327 if (smbprofile_state
.internal
.db
== NULL
) {
331 ret
= tdb_chainlock(smbprofile_state
.internal
.db
->tdb
, key
);
335 ret
= tdb_parse_record(smbprofile_state
.internal
.db
->tdb
,
336 key
, profile_stats_parser
, &s
);
338 tdb_chainunlock(smbprofile_state
.internal
.db
->tdb
, key
);
341 tdb_delete(smbprofile_state
.internal
.db
->tdb
, key
);
342 tdb_chainunlock(smbprofile_state
.internal
.db
->tdb
, key
);
345 ret
= tdb_chainlock(smbprofile_state
.internal
.db
->tdb
, key
);
349 tdb_parse_record(smbprofile_state
.internal
.db
->tdb
,
350 key
, profile_stats_parser
, &acc
);
353 * We may have to fix the disconnect count
354 * in case the process died
356 s
.values
.disconnect_stats
.count
= s
.values
.connect_stats
.count
;
358 smbprofile_stats_accumulate(&acc
, &s
);
360 acc
.magic
= profile_p
->magic
;
361 tdb_store(smbprofile_state
.internal
.db
->tdb
, key
,
363 .dptr
= (uint8_t *)&acc
,
368 tdb_chainunlock(smbprofile_state
.internal
.db
->tdb
, key
);
371 void smbprofile_stats_accumulate(struct profile_stats
*acc
,
372 const struct profile_stats
*add
)
374 #define SMBPROFILE_STATS_START
375 #define SMBPROFILE_STATS_SECTION_START(name, display)
376 #define SMBPROFILE_STATS_COUNT(name) do { \
377 acc->values.name##_stats.count += add->values.name##_stats.count; \
379 #define SMBPROFILE_STATS_TIME(name) do { \
380 acc->values.name##_stats.time += add->values.name##_stats.time; \
382 #define SMBPROFILE_STATS_BASIC(name) do { \
383 acc->values.name##_stats.count += add->values.name##_stats.count; \
384 acc->values.name##_stats.time += add->values.name##_stats.time; \
386 #define SMBPROFILE_STATS_BYTES(name) do { \
387 acc->values.name##_stats.count += add->values.name##_stats.count; \
388 acc->values.name##_stats.time += add->values.name##_stats.time; \
389 acc->values.name##_stats.idle += add->values.name##_stats.idle; \
390 acc->values.name##_stats.bytes += add->values.name##_stats.bytes; \
392 #define SMBPROFILE_STATS_IOBYTES(name) do { \
393 acc->values.name##_stats.count += add->values.name##_stats.count; \
394 acc->values.name##_stats.time += add->values.name##_stats.time; \
395 acc->values.name##_stats.idle += add->values.name##_stats.idle; \
396 acc->values.name##_stats.inbytes += add->values.name##_stats.inbytes; \
397 acc->values.name##_stats.outbytes += add->values.name##_stats.outbytes; \
399 #define SMBPROFILE_STATS_SECTION_END
400 #define SMBPROFILE_STATS_END
401 SMBPROFILE_STATS_ALL_SECTIONS
402 #undef SMBPROFILE_STATS_START
403 #undef SMBPROFILE_STATS_SECTION_START
404 #undef SMBPROFILE_STATS_COUNT
405 #undef SMBPROFILE_STATS_TIME
406 #undef SMBPROFILE_STATS_BASIC
407 #undef SMBPROFILE_STATS_BYTES
408 #undef SMBPROFILE_STATS_IOBYTES
409 #undef SMBPROFILE_STATS_SECTION_END
410 #undef SMBPROFILE_STATS_END
413 static int smbprofile_collect_fn(struct tdb_context
*tdb
,
414 TDB_DATA key
, TDB_DATA value
,
417 struct profile_stats
*acc
= (struct profile_stats
*)private_data
;
418 const struct profile_stats
*v
;
420 if (value
.dsize
!= sizeof(struct profile_stats
)) {
424 v
= (const struct profile_stats
*)value
.dptr
;
426 if (v
->magic
!= profile_p
->magic
) {
430 smbprofile_stats_accumulate(acc
, v
);
434 void smbprofile_collect(struct profile_stats
*stats
)
436 *stats
= (struct profile_stats
) {};
438 if (smbprofile_state
.internal
.db
== NULL
) {
442 tdb_traverse_read(smbprofile_state
.internal
.db
->tdb
,
443 smbprofile_collect_fn
, stats
);