/*
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 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 .
*/
#include "includes.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"
#ifdef HAVE_SYS_RESOURCE_H
#include
#endif
struct profile_stats *profile_p;
struct smbprofile_global_state smbprofile_state;
/****************************************************************************
Set a profiling level.
****************************************************************************/
void set_profile_level(int level, const struct server_id *src)
{
SMB_ASSERT(smbprofile_state.internal.db != NULL);
switch (level) {
case 0: /* turn off profiling */
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 */
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 */
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 */
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;
}
}
/****************************************************************************
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(struct messaging_context *msg_ctx, bool rdonly)
{
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);
TALLOC_FREE(db_name);
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);
}
profile_p = &smbprofile_state.stats.global;
rc = smbprofile_magic(profile_p, &profile_p->magic);
if (rc != 0) {
goto out;
}
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 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 = 0;
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;
}
pid = tevent_cached_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, &s);
smbprofile_stats_accumulate(profile_p, &s);
#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 */
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_collect(struct profile_stats *stats)
{
if (smbprofile_state.internal.db == NULL) {
return;
}
smbprofile_collect_tdb(smbprofile_state.internal.db->tdb,
profile_p->magic,
stats);
}