X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source3%2Fprofile%2Fprofile.c;h=90a30f01f58f7fdbbe56192d29354057931d50fe;hb=6fe2193b17ac2d57c559d3b936b37238d06d6be8;hp=c639f17ce5024ae2fc80eddb6288e4aff79c6957;hpb=8719c27726d3412edd0781beb956f48f76a62fb6;p=vlendec%2Fsamba-autobuild%2F.git diff --git a/source3/profile/profile.c b/source3/profile/profile.c index c639f17ce50..90a30f01f58 100644 --- a/source3/profile/profile.c +++ b/source3/profile/profile.c @@ -1,142 +1,457 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. +/* + Unix SMB/CIFS implementation. store smbd profiling information in shared memory Copyright (C) Andrew Tridgell 1999 - + Copyright (C) James Peach 2006 + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program. If not, see . */ #include "includes.h" +#include "system/shmem.h" +#include "system/filesys.h" +#include "system/time.h" +#include "messages.h" +#include "smbprofile.h" +#include "lib/tdb_wrap/tdb_wrap.h" +#include +#include "../lib/crypto/crypto.h" -#include - -extern int DEBUGLEVEL; +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif -#define IPC_PERMS ((SHM_R | SHM_W) | (SHM_R>>3) | (SHM_R>>6)) +#include +#include -static int shm_id; -static BOOL read_only; - -struct profile_header *profile_h; struct profile_stats *profile_p; - -BOOL do_profile_flag = False; -BOOL do_profile_times = False; - -struct timeval profile_starttime; -struct timeval profile_endtime; -struct timeval profile_starttime_nested; -struct timeval profile_endtime_nested; +struct smbprofile_global_state smbprofile_state; /**************************************************************************** -receive a set profile level message +Set a profiling level. ****************************************************************************/ -void profile_message(int msg_type, pid_t src, void *buf, size_t len) +void set_profile_level(int level, const struct server_id *src) { - int level; + SMB_ASSERT(smbprofile_state.internal.db != NULL); - memcpy(&level, buf, sizeof(int)); switch (level) { case 0: /* turn off profiling */ - do_profile_flag = False; - do_profile_times = False; + smbprofile_state.config.do_count = false; + smbprofile_state.config.do_times = false; + DEBUG(1,("INFO: Profiling turned OFF from pid %d\n", + (int)procid_to_pid(src))); break; case 1: /* turn on counter profiling only */ - do_profile_flag = True; - do_profile_times = False; + smbprofile_state.config.do_count = true; + smbprofile_state.config.do_times = false; + DEBUG(1,("INFO: Profiling counts turned ON from pid %d\n", + (int)procid_to_pid(src))); break; case 2: /* turn on complete profiling */ - do_profile_flag = True; - do_profile_times = True; + smbprofile_state.config.do_count = true; + smbprofile_state.config.do_times = true; + DEBUG(1,("INFO: Full profiling turned ON from pid %d\n", + (int)procid_to_pid(src))); break; case 3: /* reset profile values */ - memset((char *)profile_p, 0, sizeof(*profile_p)); + ZERO_STRUCT(profile_p->values); + tdb_wipe_all(smbprofile_state.internal.db->tdb); + DEBUG(1,("INFO: Profiling values cleared from pid %d\n", + (int)procid_to_pid(src))); break; } - DEBUG(1,("Profile level set to %d from pid %d\n", level, (int)src)); +} + +/**************************************************************************** +receive a set profile level message +****************************************************************************/ +static void profile_message(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id src, + DATA_BLOB *data) +{ + int level; + + if (data->length != sizeof(level)) { + DEBUG(0, ("got invalid profile message\n")); + return; + } + + memcpy(&level, data->data, sizeof(level)); + set_profile_level(level, &src); +} + +/**************************************************************************** +receive a request profile level message +****************************************************************************/ +static void reqprofile_message(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id src, + DATA_BLOB *data) +{ + int level; + + level = 1; + if (smbprofile_state.config.do_count) { + level += 2; + } + if (smbprofile_state.config.do_times) { + level += 4; + } + + DEBUG(1,("INFO: Received REQ_PROFILELEVEL message from PID %u\n", + (unsigned int)procid_to_pid(&src))); + messaging_send_buf(msg_ctx, src, MSG_PROFILELEVEL, + (uint8_t *)&level, sizeof(level)); } /******************************************************************* open the profiling shared memory area ******************************************************************/ -BOOL profile_setup(BOOL rdonly) +bool profile_setup(struct messaging_context *msg_ctx, bool rdonly) +{ + uint8_t digest[gnutls_hash_get_len(GNUTLS_DIG_SHA1)]; + gnutls_hash_hd_t hash_hnd = NULL; + char *db_name; + bool ok = false; + int rc; + + if (smbprofile_state.internal.db != NULL) { + return true; + } + + db_name = cache_path(talloc_tos(), "smbprofile.tdb"); + if (db_name == NULL) { + return false; + } + + smbprofile_state.internal.db = tdb_wrap_open( + NULL, db_name, 0, + rdonly ? 0 : TDB_CLEAR_IF_FIRST|TDB_MUTEX_LOCKING, + O_CREAT | (rdonly ? O_RDONLY : O_RDWR), 0644); + if (smbprofile_state.internal.db == NULL) { + return false; + } + + if (msg_ctx != NULL) { + messaging_register(msg_ctx, NULL, MSG_PROFILE, + profile_message); + messaging_register(msg_ctx, NULL, MSG_REQ_PROFILELEVEL, + reqprofile_message); + } + + rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_SHA1); + if (rc < 0) { + goto out; + } + rc = gnutls_hash(hash_hnd, + &smbprofile_state.stats.global, + sizeof(smbprofile_state.stats.global)); + +#define __UPDATE(str) do { \ + rc |= gnutls_hash(hash_hnd, str, strlen(str)); \ +} while(0) +#define SMBPROFILE_STATS_START +#define SMBPROFILE_STATS_SECTION_START(name, display) do { \ + __UPDATE(#name "+" #display); \ +} while(0); +#define SMBPROFILE_STATS_COUNT(name) do { \ + __UPDATE(#name "+count"); \ +} while(0); +#define SMBPROFILE_STATS_TIME(name) do { \ + __UPDATE(#name "+time"); \ +} while(0); +#define SMBPROFILE_STATS_BASIC(name) do { \ + __UPDATE(#name "+count"); \ + __UPDATE(#name "+time"); \ +} while(0); +#define SMBPROFILE_STATS_BYTES(name) do { \ + __UPDATE(#name "+count"); \ + __UPDATE(#name "+time"); \ + __UPDATE(#name "+idle"); \ + __UPDATE(#name "+bytes"); \ +} while(0); +#define SMBPROFILE_STATS_IOBYTES(name) do { \ + __UPDATE(#name "+count"); \ + __UPDATE(#name "+time"); \ + __UPDATE(#name "+idle"); \ + __UPDATE(#name "+inbytes"); \ + __UPDATE(#name "+outbytes"); \ +} while(0); +#define SMBPROFILE_STATS_SECTION_END +#define SMBPROFILE_STATS_END + SMBPROFILE_STATS_ALL_SECTIONS +#undef __UPDATE +#undef SMBPROFILE_STATS_START +#undef SMBPROFILE_STATS_SECTION_START +#undef SMBPROFILE_STATS_COUNT +#undef SMBPROFILE_STATS_TIME +#undef SMBPROFILE_STATS_BASIC +#undef SMBPROFILE_STATS_BYTES +#undef SMBPROFILE_STATS_IOBYTES +#undef SMBPROFILE_STATS_SECTION_END +#undef SMBPROFILE_STATS_END + if (rc != 0) { + gnutls_hash_deinit(hash_hnd, NULL); + goto out; + } + + gnutls_hash_deinit(hash_hnd, digest); + + profile_p = &smbprofile_state.stats.global; + + profile_p->magic = BVAL(digest, 0); + if (profile_p->magic == 0) { + profile_p->magic = BVAL(digest, 8); + } + + ok = true; +out: + return ok; +} + +void smbprofile_dump_setup(struct tevent_context *ev) +{ + TALLOC_FREE(smbprofile_state.internal.te); + smbprofile_state.internal.ev = ev; +} + +static void smbprofile_dump_timer(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + smbprofile_dump(); +} + +void smbprofile_dump_schedule_timer(void) +{ + struct timeval tv; + + GetTimeOfDay(&tv); + tv.tv_sec += 1; + + smbprofile_state.internal.te = tevent_add_timer( + smbprofile_state.internal.ev, + smbprofile_state.internal.ev, + tv, + smbprofile_dump_timer, + NULL); +} + +static int profile_stats_parser(TDB_DATA key, TDB_DATA value, + void *private_data) { - struct shmid_ds shm_ds; - - read_only = rdonly; - - again: - /* try to use an existing key */ - shm_id = shmget(PROF_SHMEM_KEY, 0, 0); - - /* if that failed then create one. There is a race condition here - if we are running from inetd. Bad luck. */ - if (shm_id == -1) { - if (read_only) return False; - shm_id = shmget(PROF_SHMEM_KEY, sizeof(*profile_h), - IPC_CREAT | IPC_EXCL | IPC_PERMS); - } - - if (shm_id == -1) { - DEBUG(0,("Can't create or use IPC area. Error was %s\n", - strerror(errno))); - return False; - } - - - profile_h = (struct profile_header *)shmat(shm_id, 0, - read_only?SHM_RDONLY:0); - if ((long)profile_p == -1) { - DEBUG(0,("Can't attach to IPC area. Error was %s\n", - strerror(errno))); - return False; - } - - /* find out who created this memory area */ - if (shmctl(shm_id, IPC_STAT, &shm_ds) != 0) { - DEBUG(0,("ERROR shmctl : can't IPC_STAT. Error was %s\n", - strerror(errno))); - return False; - } - - if (shm_ds.shm_perm.cuid != 0 || shm_ds.shm_perm.cgid != 0) { - DEBUG(0,("ERROR: root did not create the shmem\n")); - return False; - } - - if (shm_ds.shm_segsz != sizeof(*profile_h)) { - DEBUG(0,("WARNING: profile size is %d (expected %d). Deleting\n", - (int)shm_ds.shm_segsz, sizeof(*profile_h))); - if (shmctl(shm_id, IPC_RMID, &shm_ds) == 0) { - goto again; - } else { - return False; - } - } - - if (!read_only && (shm_ds.shm_nattch == 1)) { - memset((char *)profile_h, 0, sizeof(*profile_h)); - profile_h->prof_shm_magic = PROF_SHM_MAGIC; - profile_h->prof_shm_version = PROF_SHM_VERSION; - DEBUG(3,("Initialised profile area\n")); - } - - profile_p = &profile_h->stats; - message_register(MSG_PROFILE, profile_message); - return True; + struct profile_stats *s = private_data; + + if (value.dsize != sizeof(struct profile_stats)) { + *s = (struct profile_stats) {}; + return 0; + } + + memcpy(s, value.dptr, value.dsize); + if (s->magic != profile_p->magic) { + *s = (struct profile_stats) {}; + return 0; + } + + return 0; } +void smbprofile_dump(void) +{ + pid_t pid = getpid(); + TDB_DATA key = { .dptr = (uint8_t *)&pid, .dsize = sizeof(pid) }; + struct profile_stats s = {}; + int ret; +#ifdef HAVE_GETRUSAGE + struct rusage rself; +#endif /* HAVE_GETRUSAGE */ + + TALLOC_FREE(smbprofile_state.internal.te); + + if (! (smbprofile_state.config.do_count || + smbprofile_state.config.do_times)) { + return; + } + + if (smbprofile_state.internal.db == NULL) { + return; + } + +#ifdef HAVE_GETRUSAGE + ret = getrusage(RUSAGE_SELF, &rself); + if (ret != 0) { + ZERO_STRUCT(rself); + } + + profile_p->values.cpu_user_stats.time = + (rself.ru_utime.tv_sec * 1000000) + + rself.ru_utime.tv_usec; + profile_p->values.cpu_system_stats.time = + (rself.ru_stime.tv_sec * 1000000) + + rself.ru_stime.tv_usec; +#endif /* HAVE_GETRUSAGE */ + + ret = tdb_chainlock(smbprofile_state.internal.db->tdb, key); + if (ret != 0) { + return; + } + + tdb_parse_record(smbprofile_state.internal.db->tdb, + key, profile_stats_parser, &s); + + smbprofile_stats_accumulate(profile_p, &s); + + tdb_store(smbprofile_state.internal.db->tdb, key, + (TDB_DATA) { + .dptr = (uint8_t *)profile_p, + .dsize = sizeof(*profile_p) + }, + 0); + + tdb_chainunlock(smbprofile_state.internal.db->tdb, key); + ZERO_STRUCT(profile_p->values); + + return; +} + +void smbprofile_cleanup(pid_t pid, pid_t dst) +{ + TDB_DATA key = { .dptr = (uint8_t *)&pid, .dsize = sizeof(pid) }; + struct profile_stats s = {}; + struct profile_stats acc = {}; + int ret; + + if (smbprofile_state.internal.db == NULL) { + return; + } + + ret = tdb_chainlock(smbprofile_state.internal.db->tdb, key); + if (ret != 0) { + return; + } + ret = tdb_parse_record(smbprofile_state.internal.db->tdb, + key, profile_stats_parser, &s); + if (ret == -1) { + tdb_chainunlock(smbprofile_state.internal.db->tdb, key); + return; + } + tdb_delete(smbprofile_state.internal.db->tdb, key); + tdb_chainunlock(smbprofile_state.internal.db->tdb, key); + + pid = dst; + ret = tdb_chainlock(smbprofile_state.internal.db->tdb, key); + if (ret != 0) { + return; + } + tdb_parse_record(smbprofile_state.internal.db->tdb, + key, profile_stats_parser, &acc); + + /* + * We may have to fix the disconnect count + * in case the process died + */ + s.values.disconnect_stats.count = s.values.connect_stats.count; + + smbprofile_stats_accumulate(&acc, &s); + + acc.magic = profile_p->magic; + tdb_store(smbprofile_state.internal.db->tdb, key, + (TDB_DATA) { + .dptr = (uint8_t *)&acc, + .dsize = sizeof(acc) + }, + 0); + + tdb_chainunlock(smbprofile_state.internal.db->tdb, key); +} + +void smbprofile_stats_accumulate(struct profile_stats *acc, + const struct profile_stats *add) +{ +#define SMBPROFILE_STATS_START +#define SMBPROFILE_STATS_SECTION_START(name, display) +#define SMBPROFILE_STATS_COUNT(name) do { \ + acc->values.name##_stats.count += add->values.name##_stats.count; \ +} while(0); +#define SMBPROFILE_STATS_TIME(name) do { \ + acc->values.name##_stats.time += add->values.name##_stats.time; \ +} while(0); +#define SMBPROFILE_STATS_BASIC(name) do { \ + acc->values.name##_stats.count += add->values.name##_stats.count; \ + acc->values.name##_stats.time += add->values.name##_stats.time; \ +} while(0); +#define SMBPROFILE_STATS_BYTES(name) do { \ + acc->values.name##_stats.count += add->values.name##_stats.count; \ + acc->values.name##_stats.time += add->values.name##_stats.time; \ + acc->values.name##_stats.idle += add->values.name##_stats.idle; \ + acc->values.name##_stats.bytes += add->values.name##_stats.bytes; \ +} while(0); +#define SMBPROFILE_STATS_IOBYTES(name) do { \ + acc->values.name##_stats.count += add->values.name##_stats.count; \ + acc->values.name##_stats.time += add->values.name##_stats.time; \ + acc->values.name##_stats.idle += add->values.name##_stats.idle; \ + acc->values.name##_stats.inbytes += add->values.name##_stats.inbytes; \ + acc->values.name##_stats.outbytes += add->values.name##_stats.outbytes; \ +} while(0); +#define SMBPROFILE_STATS_SECTION_END +#define SMBPROFILE_STATS_END + SMBPROFILE_STATS_ALL_SECTIONS +#undef SMBPROFILE_STATS_START +#undef SMBPROFILE_STATS_SECTION_START +#undef SMBPROFILE_STATS_COUNT +#undef SMBPROFILE_STATS_TIME +#undef SMBPROFILE_STATS_BASIC +#undef SMBPROFILE_STATS_BYTES +#undef SMBPROFILE_STATS_IOBYTES +#undef SMBPROFILE_STATS_SECTION_END +#undef SMBPROFILE_STATS_END +} + +static int smbprofile_collect_fn(struct tdb_context *tdb, + TDB_DATA key, TDB_DATA value, + void *private_data) +{ + struct profile_stats *acc = (struct profile_stats *)private_data; + const struct profile_stats *v; + + if (value.dsize != sizeof(struct profile_stats)) { + return 0; + } + + v = (const struct profile_stats *)value.dptr; + + if (v->magic != profile_p->magic) { + return 0; + } + + smbprofile_stats_accumulate(acc, v); + return 0; +} + +void smbprofile_collect(struct profile_stats *stats) +{ + *stats = (struct profile_stats) {}; + + if (smbprofile_state.internal.db == NULL) { + return; + } + + tdb_traverse_read(smbprofile_state.internal.db->tdb, + smbprofile_collect_fn, stats); +}