diff options
Diffstat (limited to 'source3/profile')
-rw-r--r-- | source3/profile/profile.c | 381 |
1 files changed, 301 insertions, 80 deletions
diff --git a/source3/profile/profile.c b/source3/profile/profile.c index c7206385027..7002537354c 100644 --- a/source3/profile/profile.c +++ b/source3/profile/profile.c @@ -24,54 +24,42 @@ #include "system/filesys.h" #include "messages.h" #include "smbprofile.h" +#include "lib/tdb_wrap/tdb_wrap.h" +#include <tevent.h> +#include "../lib/crypto/crypto.h" -#define PROF_SHMEM_KEY ((key_t)0x07021999) -#define PROF_SHM_MAGIC 0x6349985 -#define PROF_SHM_VERSION 15 - -#define IPC_PERMS ((S_IRUSR | S_IWUSR) | S_IRGRP | S_IROTH) - -static int shm_id; -static bool read_only; - -struct profile_header { - int prof_shm_magic; - int prof_shm_version; - struct profile_stats stats; -}; - -static struct profile_header *profile_h; struct profile_stats *profile_p; - -bool do_profile_flag = False; -bool do_profile_times = False; +struct smbprofile_global_state smbprofile_state; /**************************************************************************** Set a profiling level. ****************************************************************************/ void set_profile_level(int level, struct server_id src) { + SMB_ASSERT(smbprofile_state.internal.db != NULL); + 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; @@ -109,7 +97,13 @@ static void reqprofile_message(struct messaging_context *msg_ctx, { int level; - level = 1 + (do_profile_flag?2:0) + (do_profile_times?4:0); + 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))); @@ -122,75 +116,302 @@ static void reqprofile_message(struct messaging_context *msg_ctx, ******************************************************************/ bool profile_setup(struct messaging_context *msg_ctx, bool rdonly) { - struct shmid_ds shm_ds; + unsigned char tmp[16] = {}; + MD5_CTX md5; + char *db_name; - read_only = rdonly; + if (smbprofile_state.internal.db != NULL) { + return true; + } - again: - /* try to use an existing key */ - shm_id = shmget(PROF_SHMEM_KEY, 0, 0); + db_name = cache_path("smbprofile.tdb"); + if (db_name == NULL) { + return false; + } - /* 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); + 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 (shm_id == -1) { - DEBUG(0,("Can't create or use IPC area. Error was %s\n", - strerror(errno))); - 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); } - profile_h = (struct profile_header *)shmat(shm_id, 0, - read_only?SHM_RDONLY:0); - if ((long)profile_h == -1) { - DEBUG(0,("Can't attach to IPC area. Error was %s\n", - strerror(errno))); - return False; + MD5Init(&md5); + + MD5Update(&md5, + (const uint8_t *)&smbprofile_state.stats.global, + sizeof(smbprofile_state.stats.global)); + +#define __UPDATE(str) do { \ + MD5Update(&md5, (const uint8_t *)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 + + MD5Final(tmp, &md5); + + profile_p = &smbprofile_state.stats.global; + + profile_p->magic = BVAL(tmp, 0); + if (profile_p->magic == 0) { + profile_p->magic = BVAL(tmp, 8); } - /* 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; + return True; +} + +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 profile_stats *s = private_data; + + if (value.dsize != sizeof(struct profile_stats)) { + *s = (struct profile_stats) {}; + return 0; } - if (shm_ds.shm_perm.cuid != sec_initial_uid() || - shm_ds.shm_perm.cgid != sec_initial_gid()) { - DEBUG(0,("ERROR: we did not create the shmem " - "(owned by another user, uid %u, gid %u)\n", - shm_ds.shm_perm.cuid, - shm_ds.shm_perm.cgid)); - return False; + memcpy(s, value.dptr, value.dsize); + if (s->magic != profile_p->magic) { + *s = (struct profile_stats) {}; + return 0; } - if (shm_ds.shm_segsz != sizeof(*profile_h)) { - DEBUG(0,("WARNING: profile size is %d (expected %d). Deleting\n", - (int)shm_ds.shm_segsz, (int)sizeof(*profile_h))); - if (shmctl(shm_id, IPC_RMID, &shm_ds) == 0) { - goto again; - } else { - return False; - } + 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; + + TALLOC_FREE(smbprofile_state.internal.te); + + if (smbprofile_state.internal.db == NULL) { + return; } - 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")); + ret = tdb_chainlock(smbprofile_state.internal.db->tdb, key); + if (ret != 0) { + return; } - profile_p = &profile_h->stats; - if (msg_ctx != NULL) { - messaging_register(msg_ctx, NULL, MSG_PROFILE, - profile_message); - messaging_register(msg_ctx, NULL, MSG_REQ_PROFILELEVEL, - reqprofile_message); + 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) +{ + 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; } - return True; + + 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 = getpid(); + 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); } |