s3:smbprofile: Replace sysv shmem with tdb
authorVolker Lendecke <vl@samba.org>
Mon, 29 Sep 2014 16:08:17 +0000 (16:08 +0000)
committerRalph Böhme <slow@samba.org>
Fri, 6 Mar 2015 11:31:10 +0000 (12:31 +0100)
What?

This patch gets rid of the central shared memory segment referenced by
"profile_p". Instead, every smbd gets a static profile_area where it collects
profiling data. Once a second, every smbd writes this profiling data into a
record of its own in a "smbprofile.tdb". smbstatus -P does a tdb_traverse on this
database and sums up what it finds.

Why?

At least in my perception sysv IPC has not the best reputation on earth. The
code before this patch uses shmat(). Samba ages ago has developed a good
abstraction of shared memory: It's called tdb.

The main reason why I started this is that I have a request to become
more flexible with profiling data. Samba should be able to collect data
per share or per user, something which is almost impossible to do with
a fixed structure. My idea is to for example install a profile area per
share and every second marshall this into one tdb record indexed by share
name. smbstatus -P would then also collect the data and either aggregate
them or put them into individual per-share statistics. This flexibility
in the data model is not really possible with one fixed structure.

But isn't it slow?

Well, I don't think so. I can't really prove it, but I do believe that on large
boxes atomically incrementing a shared memory value for every SMB does show up
due to NUMA effects. With this patch the hot code path is completely
process-local. Once a second every smbd writes into a central tdb, this of
course does atomic operations. But it's once a second, not on every SMB2 read.

There's two places where I would like to improve things: With the current code
all smbds wake up once a second. With 10,000 potentially idle smbds this will
become noticable. That's why the current only starts the timer when something has
changed.

The second place is the tdb traverse: Right now traverse is blocking in the
sense that when it has to switch hash chains it will block. With mutexes, this
means a syscall. I have a traverse light in mind that works as follows: It
assumes a locked hash chain and then walks the complete chain in one run
without unlocking in between. This way the caller can do nonblocking locks in
the first round and only do blocking locks in a second round. Also, a lot of
syscall overhead will vanish. This way smbstatus -P will have almost zero
impact on normal operations.

Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>

Signed-off-by: Volker Lendecke <vl@samba.org>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
source3/include/smbprofile.h
source3/profile/profile.c
source3/smbd/process.c
source3/smbd/server.c
source3/smbd/server_exit.c
source3/smbd/smb2_server.c
source3/utils/status_profile.c

index bf2b3b456f7aecf90d3db0ab1a09100ebc7384a6..b32d938306188a3ac6a984198ab644e591fd2ed1 100644 (file)
@@ -21,6 +21,8 @@
 
 */
 
+struct tevent_context;
+
 #ifdef WITH_PROFILE
 
 #define SMBPROFILE_STATS_ALL_SECTIONS \
@@ -322,6 +324,8 @@ struct smbprofile_stats_iobytes_async {
 };
 
 struct profile_stats {
+       uint64_t magic;
+       struct {
 #define SMBPROFILE_STATS_START
 #define SMBPROFILE_STATS_SECTION_START(name, display)
 #define SMBPROFILE_STATS_COUNT(name) \
@@ -346,11 +350,13 @@ struct profile_stats {
 #undef SMBPROFILE_STATS_IOBYTES
 #undef SMBPROFILE_STATS_SECTION_END
 #undef SMBPROFILE_STATS_END
+       } values;
 };
 
 #define _SMBPROFILE_COUNT_INCREMENT(_stats, _area, _v) do { \
-       if (do_profile_flag) { \
-               (_area)->_stats.count += (_v); \
+       if (smbprofile_state.config.do_count) { \
+               (_area)->values._stats.count += (_v); \
+               smbprofile_dump_schedule(); \
        } \
 } while(0)
 #define SMBPROFILE_COUNT_INCREMENT(_name, _area, _v) \
@@ -361,7 +367,7 @@ struct profile_stats {
 #define _SMBPROFILE_TIME_ASYNC_START(_stats, _area, _async) do { \
        (_async) = (struct smbprofile_stats_time_async) {}; \
        if (smbprofile_state.config.do_times) { \
-               (_async).stats = &((_area)->_stats), \
+               (_async).stats = &((_area)->values._stats), \
                (_async).start = profile_timestamp(); \
        } \
 } while(0)
@@ -371,6 +377,7 @@ struct profile_stats {
        if ((_async).start != 0) { \
                (_async).stats->time += profile_timestamp() - (_async).start; \
                (_async) = (struct smbprofile_stats_basic_async) {}; \
+               smbprofile_dump_schedule(); \
        } \
 } while(0)
 
@@ -378,12 +385,13 @@ struct profile_stats {
        struct smbprofile_stats_basic_async _async_name;
 #define _SMBPROFILE_BASIC_ASYNC_START(_stats, _area, _async) do { \
        (_async) = (struct smbprofile_stats_basic_async) {}; \
-       if (do_profile_flag) { \
-               if (do_profile_times) { \
+       if (smbprofile_state.config.do_count) { \
+               if (smbprofile_state.config.do_times) { \
                        (_async).start = profile_timestamp(); \
-                       (_async).stats = &((_area)->_stats); \
+                       (_async).stats = &((_area)->values._stats); \
                } \
-               (_area)->_stats.count += 1; \
+               (_area)->values._stats.count += 1; \
+               smbprofile_dump_schedule(); \
        } \
 } while(0)
 #define SMBPROFILE_BASIC_ASYNC_START(_name, _area, _async) \
@@ -392,12 +400,13 @@ struct profile_stats {
        if ((_async).start != 0) { \
                (_async).stats->time += profile_timestamp() - (_async).start; \
                (_async) = (struct smbprofile_stats_basic_async) {}; \
+               smbprofile_dump_schedule(); \
        } \
 } while(0)
 
 #define _SMBPROFILE_TIMER_ASYNC_START(_stats, _area, _async) do { \
-       (_async).stats = &((_area)->_stats); \
-       if (do_profile_times) { \
+       (_async).stats = &((_area)->values._stats); \
+       if (smbprofile_state.config.do_times) { \
                (_async).start = profile_timestamp(); \
        } \
 } while(0)
@@ -427,10 +436,11 @@ struct profile_stats {
        struct smbprofile_stats_bytes_async _async_name;
 #define _SMBPROFILE_BYTES_ASYNC_START(_stats, _area, _async, _bytes) do { \
        (_async) = (struct smbprofile_stats_bytes_async) {}; \
-       if (do_profile_flag) { \
+       if (smbprofile_state.config.do_count) { \
                _SMBPROFILE_TIMER_ASYNC_START(_stats, _area, _async); \
-               (_area)->_stats.count += 1; \
-               (_area)->_stats.bytes += (_bytes); \
+               (_area)->values._stats.count += 1; \
+               (_area)->values._stats.bytes += (_bytes); \
+               smbprofile_dump_schedule(); \
        } \
 } while(0)
 #define SMBPROFILE_BYTES_ASYNC_START(_name, _area, _async, _bytes) \
@@ -443,6 +453,7 @@ struct profile_stats {
        if ((_async).stats != NULL) { \
                _SMBPROFILE_TIMER_ASYNC_END(_async); \
                (_async) = (struct smbprofile_stats_bytes_async) {}; \
+               smbprofile_dump_schedule(); \
        } \
 } while(0)
 
@@ -450,10 +461,11 @@ struct profile_stats {
        struct smbprofile_stats_iobytes_async _async_name;
 #define _SMBPROFILE_IOBYTES_ASYNC_START(_stats, _area, _async, _inbytes) do { \
        (_async) = (struct smbprofile_stats_iobytes_async) {}; \
-       if (do_profile_flag) { \
+       if (smbprofile_state.config.do_count) { \
                _SMBPROFILE_TIMER_ASYNC_START(_stats, _area, _async); \
-               (_area)->_stats.count += 1; \
-               (_area)->_stats.inbytes += (_inbytes); \
+               (_area)->values._stats.count += 1; \
+               (_area)->values._stats.inbytes += (_inbytes); \
+               smbprofile_dump_schedule(); \
        } \
 } while(0)
 #define SMBPROFILE_IOBYTES_ASYNC_START(_name, _area, _async, _inbytes) \
@@ -467,12 +479,62 @@ struct profile_stats {
                (_async).stats->outbytes += (_outbytes); \
                _SMBPROFILE_TIMER_ASYNC_END(_async); \
                (_async) = (struct smbprofile_stats_iobytes_async) {}; \
+               smbprofile_dump_schedule(); \
        } \
 } while(0)
 
 extern struct profile_stats *profile_p;
-extern bool do_profile_flag;
-extern bool do_profile_times;
+
+struct smbprofile_global_state {
+       struct {
+               struct tdb_wrap *db;
+               struct tevent_context *ev;
+               struct tevent_timer *te;
+       } internal;
+
+       struct {
+               bool do_count;
+               bool do_times;
+       } config;
+
+       struct {
+               struct profile_stats global;
+       } stats;
+};
+
+extern struct smbprofile_global_state smbprofile_state;
+
+void smbprofile_dump_schedule_timer(void);
+void smbprofile_dump_setup(struct tevent_context *ev);
+
+static inline void smbprofile_dump_schedule(void)
+{
+       if (likely(smbprofile_state.internal.te != NULL)) {
+               return;
+       }
+
+       if (unlikely(smbprofile_state.internal.ev == NULL)) {
+               return;
+       }
+
+       smbprofile_dump_schedule_timer();
+}
+
+static inline bool smbprofile_dump_pending(void)
+{
+       if (smbprofile_state.internal.te == NULL) {
+               return false;
+       }
+
+       return true;
+}
+
+void smbprofile_dump(void);
+
+void smbprofile_cleanup(pid_t pid);
+void smbprofile_stats_accumulate(struct profile_stats *acc,
+                                const struct profile_stats *add);
+void smbprofile_collect(struct profile_stats *stats);
 
 static inline uint64_t profile_timestamp(void)
 {
@@ -531,6 +593,26 @@ static inline uint64_t profile_timestamp(void)
 #define END_PROFILE(x)
 #define END_PROFILE_BYTES(x)
 
+static inline bool smbprofile_dump_pending(void)
+{
+       return false;
+}
+
+static inline void smbprofile_dump_setup(struct tevent_context *ev)
+{
+       return;
+}
+
+static inline void smbprofile_dump(void)
+{
+       return;
+}
+
+static inline void smbprofile_cleanup(pid_t pid)
+{
+       return;
+}
+
 #endif /* WITH_PROFILE */
 
 /* The following definitions come from profile/profile.c  */
index c720638502783126045d351b41ab9031a8057034..7002537354cbfb4c772571e06cc77307e7eee0a7 100644 (file)
 #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);
 }
index a761669aefa73d3d0b9f7a0d4ec810671b999528..38edb028636ae4763d5181f2fdc097433171df0a 100644 (file)
@@ -3500,6 +3500,7 @@ NTSTATUS smbXsrv_connection_init_tables(struct smbXsrv_connection *conn,
 }
 
 struct smbd_tevent_trace_state {
+       struct tevent_context *ev;
        TALLOC_CTX *frame;
        SMBPROFILE_BASIC_ASYNC_STATE(profile_idle);
 };
@@ -3512,10 +3513,31 @@ static void smbd_tevent_trace_callback(enum tevent_trace_point point,
 
        switch (point) {
        case TEVENT_TRACE_BEFORE_WAIT:
+               if (!smbprofile_dump_pending()) {
+                       /*
+                        * If there's no dump pending
+                        * we don't want to schedule a new 1 sec timer.
+                        *
+                        * Instead we want to sleep as long as nothing happens.
+                        */
+                       smbprofile_dump_setup(NULL);
+               }
                SMBPROFILE_BASIC_ASYNC_START(idle, profile_p, state->profile_idle);
                break;
        case TEVENT_TRACE_AFTER_WAIT:
                SMBPROFILE_BASIC_ASYNC_END(state->profile_idle);
+               if (!smbprofile_dump_pending()) {
+                       /*
+                        * We need to flush our state after sleeping
+                        * (hopefully a long time).
+                        */
+                       smbprofile_dump();
+                       /*
+                        * future profiling events should trigger timers
+                        * on our main event context.
+                        */
+                       smbprofile_dump_setup(state->ev);
+               }
                break;
        case TEVENT_TRACE_BEFORE_LOOP_ONCE:
                TALLOC_FREE(state->frame);
@@ -3766,6 +3788,7 @@ void smbd_process(struct tevent_context *ev_ctx,
                  bool interactive)
 {
        struct smbd_tevent_trace_state trace_state = {
+               .ev = ev_ctx,
                .frame = talloc_stackframe(),
        };
        struct smbXsrv_client *client = NULL;
@@ -3978,6 +4001,8 @@ void smbd_process(struct tevent_context *ev_ctx,
                exit(1);
        }
 
+       smbprofile_dump_setup(ev_ctx);
+
        if (!init_dptrs(sconn)) {
                exit_server("init_dptrs() failed");
        }
index 8207bf1676f822c8649f5882351c81e7c860be45..257c13a14cc28cc1f0c18867b81b5b8925229e82 100644 (file)
@@ -428,6 +428,8 @@ static void remove_child_pid(struct smbd_parent_context *parent,
                           __func__, strerror(ret)));
        }
 
+       smbprofile_cleanup(pid);
+
        for (child = parent->children; child != NULL; child = child->next) {
                if (child->pid == pid) {
                        struct smbd_child_pid *tmp = child;
index e5d32b8fe778f9deb644d01c79e2936ee173a056..69d0fdd6bbbd6362b000a50a25d1f2c4b5140ff4 100644 (file)
@@ -45,6 +45,7 @@
 #include "serverid.h"
 #include "messages.h"
 #include "../lib/util/pidfile.h"
+#include "smbprofile.h"
 
 static struct files_struct *log_writeable_file_fn(
        struct files_struct *fsp, void *private_data)
@@ -232,6 +233,7 @@ static void exit_server_common(enum server_exit_reason how,
        xconn = NULL;
        client = NULL;
        TALLOC_FREE(global_smbXsrv_client);
+       smbprofile_dump();
        server_messaging_context_free();
        server_event_context_free();
        TALLOC_FREE(smbd_memcache_ctx);
index 432b866531f1125fdda38ce02eb949f37884ba63..d174fe69d923baf6210b26976f81e50f4b84fa11 100644 (file)
@@ -3090,8 +3090,8 @@ void smbd_smb2_first_negprot(struct smbXsrv_connection *xconn,
         * this was already counted at the SMB1 layer =>
         * smbd_smb2_request_dispatch() should not count it twice.
         */
-       if (profile_p->request_stats.count > 0) {
-               profile_p->request_stats.count--;
+       if (profile_p->values.request_stats.count > 0) {
+               profile_p->values.request_stats.count--;
        }
 #endif
        status = smbd_smb2_request_dispatch(req);
index a0f98d87c82b9f1d33fbc08e4bcf821da2073fba..109415d37fa70ac52776413a4fbf05d10900b4bd 100644 (file)
@@ -42,15 +42,19 @@ static void profile_separator(const char * title)
   ******************************************************************/
 bool status_profile_dump(bool verbose)
 {
+       struct profile_stats stats = {};
+
        if (!profile_setup(NULL, True)) {
                fprintf(stderr,"Failed to initialise profile memory\n");
                return False;
        }
 
+       smbprofile_collect(&stats);
+
 #define __PRINT_FIELD_LINE(name, _stats, field) do { \
        d_printf("%-59s%20ju\n", \
                 name "_" #field ":", \
-                (uintmax_t)profile_p->_stats.field); \
+                (uintmax_t)stats.values._stats.field); \
 } while(0);
 #define SMBPROFILE_STATS_START
 #define SMBPROFILE_STATS_SECTION_START(name, display) profile_separator(#display);
@@ -247,8 +251,8 @@ static uint64_t print_count_samples(
 #define SMBPROFILE_STATS_COUNT(name) do { \
        count += print_count_count_samples(buf, sizeof(buf), \
                                           #name, \
-                                          &current->name##_stats, \
-                                          &last->name##_stats, \
+                                          &current->values.name##_stats, \
+                                          &last->values.name##_stats, \
                                           delta_usec); \
 } while(0);
 #define SMBPROFILE_STATS_TIME(name) do { \
@@ -256,22 +260,22 @@ static uint64_t print_count_samples(
 #define SMBPROFILE_STATS_BASIC(name) do { \
        count += print_basic_count_samples(buf, sizeof(buf), \
                                           #name, \
-                                          &current->name##_stats, \
-                                          &last->name##_stats, \
+                                          &current->values.name##_stats, \
+                                          &last->values.name##_stats, \
                                           delta_usec); \
 } while(0);
 #define SMBPROFILE_STATS_BYTES(name) do { \
        count += print_bytes_count_samples(buf, sizeof(buf), \
                                           #name, \
-                                          &current->name##_stats, \
-                                          &last->name##_stats, \
+                                          &current->values.name##_stats, \
+                                          &last->values.name##_stats, \
                                           delta_usec); \
 } while(0);
 #define SMBPROFILE_STATS_IOBYTES(name) do { \
        count += print_iobytes_count_samples(buf, sizeof(buf), \
                                             #name, \
-                                            &current->name##_stats, \
-                                            &last->name##_stats, \
+                                            &current->values.name##_stats, \
+                                            &last->values.name##_stats, \
                                             delta_usec); \
 } while(0);
 #define SMBPROFILE_STATS_SECTION_END
@@ -318,13 +322,13 @@ bool status_profile_rates(bool verbose)
                return False;
        }
 
-       memcpy(&sample_data[last], profile_p, sizeof(*profile_p));
+       smbprofile_collect(&sample_data[last]);
        for (;;) {
                sample_time[current] = profile_timestamp();
                next_usec = sample_time[current] + sample_interval_usec;
 
                /* Take a sample. */
-               memcpy(&sample_data[current], profile_p, sizeof(*profile_p));
+               smbprofile_collect(&sample_data[current]);
 
                /* Rate convert some values and print results. */
                delta_usec = sample_time[current] - sample_time[last];