mirror of
https://github.com/samba-team/samba.git
synced 2024-12-28 07:21:54 +03:00
74a16a1094
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>
364 lines
9.4 KiB
C
364 lines
9.4 KiB
C
/*
|
|
* Unix SMB/CIFS implementation.
|
|
* status reporting
|
|
* Copyright (C) Andrew Tridgell 1994-1998
|
|
* Copyright (C) James Peach 2005-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 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "smbprofile.h"
|
|
#include "status_profile.h"
|
|
|
|
static void profile_separator(const char * title)
|
|
{
|
|
char line[79 + 1];
|
|
char * end;
|
|
|
|
snprintf(line, sizeof(line), "**** %s ", title);
|
|
|
|
for (end = line + strlen(line); end < &line[sizeof(line) -1]; ++end) {
|
|
*end = '*';
|
|
}
|
|
|
|
line[sizeof(line) - 1] = '\0';
|
|
d_printf("%s\n", line);
|
|
}
|
|
|
|
/*******************************************************************
|
|
dump the elements of the profile structure
|
|
******************************************************************/
|
|
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)stats.values._stats.field); \
|
|
} while(0);
|
|
#define SMBPROFILE_STATS_START
|
|
#define SMBPROFILE_STATS_SECTION_START(name, display) profile_separator(#display);
|
|
#define SMBPROFILE_STATS_COUNT(name) do { \
|
|
__PRINT_FIELD_LINE(#name, name##_stats, count); \
|
|
} while(0);
|
|
#define SMBPROFILE_STATS_TIME(name) do { \
|
|
__PRINT_FIELD_LINE(#name, name##_stats, time); \
|
|
} while(0);
|
|
#define SMBPROFILE_STATS_BASIC(name) do { \
|
|
__PRINT_FIELD_LINE(#name, name##_stats, count); \
|
|
__PRINT_FIELD_LINE(#name, name##_stats, time); \
|
|
} while(0);
|
|
#define SMBPROFILE_STATS_BYTES(name) do { \
|
|
__PRINT_FIELD_LINE(#name, name##_stats, count); \
|
|
__PRINT_FIELD_LINE(#name, name##_stats, time); \
|
|
__PRINT_FIELD_LINE(#name, name##_stats, idle); \
|
|
__PRINT_FIELD_LINE(#name, name##_stats, bytes); \
|
|
} while(0);
|
|
#define SMBPROFILE_STATS_IOBYTES(name) do { \
|
|
__PRINT_FIELD_LINE(#name, name##_stats, count); \
|
|
__PRINT_FIELD_LINE(#name, name##_stats, time); \
|
|
__PRINT_FIELD_LINE(#name, name##_stats, idle); \
|
|
__PRINT_FIELD_LINE(#name, name##_stats, inbytes); \
|
|
__PRINT_FIELD_LINE(#name, name##_stats, outbytes); \
|
|
} while(0);
|
|
#define SMBPROFILE_STATS_SECTION_END
|
|
#define SMBPROFILE_STATS_END
|
|
SMBPROFILE_STATS_ALL_SECTIONS
|
|
#undef __PRINT_FIELD_LINE
|
|
#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
|
|
|
|
return True;
|
|
}
|
|
|
|
/* Convert microseconds to milliseconds. */
|
|
#define usec_to_msec(s) ((s) / 1000)
|
|
/* Convert microseconds to seconds. */
|
|
#define usec_to_sec(s) ((s) / 1000000)
|
|
/* One second in microseconds. */
|
|
#define one_second_usec (1000000)
|
|
|
|
#define sample_interval_usec one_second_usec
|
|
|
|
#define percent_time(used, period) ((double)(used) / (double)(period) * 100.0 )
|
|
|
|
static uint64_t print_count_count_samples(
|
|
char *buf, const size_t buflen,
|
|
const char *name,
|
|
const struct smbprofile_stats_count * const current,
|
|
const struct smbprofile_stats_count * const last,
|
|
uint64_t delta_usec)
|
|
{
|
|
uint64_t step = current->count - last->count;
|
|
uint64_t count = 0;
|
|
|
|
if (step != 0) {
|
|
uint64_t delta_sec = usec_to_sec(delta_usec);
|
|
|
|
count++;
|
|
|
|
if (buf[0] == '\0') {
|
|
snprintf(buf, buflen,
|
|
"%s %ju/sec",
|
|
name, (uintmax_t)(step / delta_sec));
|
|
} else {
|
|
printf("%-40s %s %ju/sec\n",
|
|
buf, name, (uintmax_t)(step / delta_sec));
|
|
buf[0] = '\0';
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static uint64_t print_basic_count_samples(
|
|
char *buf, const size_t buflen,
|
|
const char *name,
|
|
const struct smbprofile_stats_basic * const current,
|
|
const struct smbprofile_stats_basic * const last,
|
|
uint64_t delta_usec)
|
|
{
|
|
uint64_t step = current->count - last->count;
|
|
uint64_t spent = current->time - last->time;
|
|
uint64_t count = 0;
|
|
|
|
if (step != 0) {
|
|
uint64_t delta_sec = usec_to_sec(delta_usec);
|
|
|
|
count++;
|
|
|
|
if (buf[0] == '\0') {
|
|
snprintf(buf, buflen,
|
|
"%s %ju/sec (%.2f%%)",
|
|
name, (uintmax_t)(step / delta_sec),
|
|
percent_time(spent, delta_usec));
|
|
} else {
|
|
printf("%-40s %s %ju/sec (%.2f%%)\n",
|
|
buf, name, (uintmax_t)(step / delta_sec),
|
|
percent_time(spent, delta_usec));
|
|
buf[0] = '\0';
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static uint64_t print_bytes_count_samples(
|
|
char *buf, const size_t buflen,
|
|
const char *name,
|
|
const struct smbprofile_stats_bytes * const current,
|
|
const struct smbprofile_stats_bytes * const last,
|
|
uint64_t delta_usec)
|
|
{
|
|
uint64_t step = current->count - last->count;
|
|
uint64_t spent = current->time - last->time;
|
|
uint64_t count = 0;
|
|
|
|
if (step != 0) {
|
|
uint64_t delta_sec = usec_to_sec(delta_usec);
|
|
|
|
count++;
|
|
|
|
if (buf[0] == '\0') {
|
|
snprintf(buf, buflen,
|
|
"%s %ju/sec (%.2f%%)",
|
|
name, (uintmax_t)(step / delta_sec),
|
|
percent_time(spent, delta_usec));
|
|
} else {
|
|
printf("%-40s %s %ju/sec (%.2f%%)\n",
|
|
buf, name, (uintmax_t)(step / delta_sec),
|
|
percent_time(spent, delta_usec));
|
|
buf[0] = '\0';
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static uint64_t print_iobytes_count_samples(
|
|
char *buf, const size_t buflen,
|
|
const char *name,
|
|
const struct smbprofile_stats_iobytes * const current,
|
|
const struct smbprofile_stats_iobytes * const last,
|
|
uint64_t delta_usec)
|
|
{
|
|
uint64_t step = current->count - last->count;
|
|
uint64_t spent = current->time - last->time;
|
|
uint64_t count = 0;
|
|
|
|
if (step != 0) {
|
|
uint64_t delta_sec = usec_to_sec(delta_usec);
|
|
|
|
count++;
|
|
|
|
if (buf[0] == '\0') {
|
|
snprintf(buf, buflen,
|
|
"%s %ju/sec (%.2f%%)",
|
|
name, (uintmax_t)(step / delta_sec),
|
|
percent_time(spent, delta_usec));
|
|
} else {
|
|
printf("%-40s %s %ju/sec (%.2f%%)\n",
|
|
buf, name, (uintmax_t)(step / delta_sec),
|
|
percent_time(spent, delta_usec));
|
|
buf[0] = '\0';
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static uint64_t print_count_samples(
|
|
const struct profile_stats * const current,
|
|
const struct profile_stats * const last,
|
|
uint64_t delta_usec)
|
|
{
|
|
uint64_t count = 0;
|
|
char buf[40] = { '\0', };
|
|
|
|
if (delta_usec == 0) {
|
|
return 0;
|
|
}
|
|
|
|
#define SMBPROFILE_STATS_START
|
|
#define SMBPROFILE_STATS_SECTION_START(name, display)
|
|
#define SMBPROFILE_STATS_COUNT(name) do { \
|
|
count += print_count_count_samples(buf, sizeof(buf), \
|
|
#name, \
|
|
¤t->values.name##_stats, \
|
|
&last->values.name##_stats, \
|
|
delta_usec); \
|
|
} while(0);
|
|
#define SMBPROFILE_STATS_TIME(name) do { \
|
|
} while(0);
|
|
#define SMBPROFILE_STATS_BASIC(name) do { \
|
|
count += print_basic_count_samples(buf, sizeof(buf), \
|
|
#name, \
|
|
¤t->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, \
|
|
¤t->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, \
|
|
¤t->values.name##_stats, \
|
|
&last->values.name##_stats, \
|
|
delta_usec); \
|
|
} 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
|
|
|
|
if (buf[0] != '\0') {
|
|
printf("%-40s\n", buf);
|
|
buf[0] = '\0';
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static struct profile_stats sample_data[2];
|
|
static uint64_t sample_time[2];
|
|
|
|
bool status_profile_rates(bool verbose)
|
|
{
|
|
uint64_t remain_usec;
|
|
uint64_t next_usec;
|
|
uint64_t delta_usec;
|
|
|
|
int last = 0;
|
|
int current = 1;
|
|
int tmp;
|
|
|
|
if (verbose) {
|
|
fprintf(stderr, "Sampling stats at %d sec intervals\n",
|
|
usec_to_sec(sample_interval_usec));
|
|
}
|
|
|
|
if (!profile_setup(NULL, True)) {
|
|
fprintf(stderr,"Failed to initialise profile memory\n");
|
|
return False;
|
|
}
|
|
|
|
smbprofile_collect(&sample_data[last]);
|
|
for (;;) {
|
|
sample_time[current] = profile_timestamp();
|
|
next_usec = sample_time[current] + sample_interval_usec;
|
|
|
|
/* Take a sample. */
|
|
smbprofile_collect(&sample_data[current]);
|
|
|
|
/* Rate convert some values and print results. */
|
|
delta_usec = sample_time[current] - sample_time[last];
|
|
|
|
if (print_count_samples(&sample_data[current],
|
|
&sample_data[last], delta_usec)) {
|
|
printf("\n");
|
|
}
|
|
|
|
/* Swap sampling buffers. */
|
|
tmp = last;
|
|
last = current;
|
|
current = tmp;
|
|
|
|
/* Delay until next sample time. */
|
|
remain_usec = next_usec - profile_timestamp();
|
|
if (remain_usec > sample_interval_usec) {
|
|
fprintf(stderr, "eek! falling behind sampling rate!\n");
|
|
} else {
|
|
if (verbose) {
|
|
fprintf(stderr,
|
|
"delaying for %lu msec\n",
|
|
(unsigned long )usec_to_msec(remain_usec));
|
|
}
|
|
|
|
usleep(remain_usec);
|
|
}
|
|
|
|
}
|
|
|
|
return True;
|
|
}
|