Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : store smbd profiling information in shared memory
4 : Copyright (C) Andrew Tridgell 1999
5 : Copyright (C) James Peach 2006
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 :
20 : */
21 :
22 : #include "includes.h"
23 : #include "system/filesys.h"
24 : #include "system/time.h"
25 : #include "messages.h"
26 : #include "smbprofile.h"
27 : #include "lib/tdb_wrap/tdb_wrap.h"
28 : #include <tevent.h>
29 : #include "../lib/crypto/crypto.h"
30 :
31 : #ifdef HAVE_SYS_RESOURCE_H
32 : #include <sys/resource.h>
33 : #endif
34 :
35 : struct profile_stats *profile_p;
36 : struct smbprofile_global_state smbprofile_state;
37 :
38 : /****************************************************************************
39 : Set a profiling level.
40 : ****************************************************************************/
41 0 : void set_profile_level(int level, const struct server_id *src)
42 : {
43 0 : SMB_ASSERT(smbprofile_state.internal.db != NULL);
44 :
45 0 : switch (level) {
46 0 : case 0: /* turn off profiling */
47 0 : smbprofile_state.config.do_count = false;
48 0 : smbprofile_state.config.do_times = false;
49 0 : DEBUG(1,("INFO: Profiling turned OFF from pid %d\n",
50 : (int)procid_to_pid(src)));
51 0 : break;
52 0 : case 1: /* turn on counter profiling only */
53 0 : smbprofile_state.config.do_count = true;
54 0 : smbprofile_state.config.do_times = false;
55 0 : DEBUG(1,("INFO: Profiling counts turned ON from pid %d\n",
56 : (int)procid_to_pid(src)));
57 0 : break;
58 0 : case 2: /* turn on complete profiling */
59 0 : smbprofile_state.config.do_count = true;
60 0 : smbprofile_state.config.do_times = true;
61 0 : DEBUG(1,("INFO: Full profiling turned ON from pid %d\n",
62 : (int)procid_to_pid(src)));
63 0 : break;
64 0 : case 3: /* reset profile values */
65 0 : ZERO_STRUCT(profile_p->values);
66 0 : tdb_wipe_all(smbprofile_state.internal.db->tdb);
67 0 : DEBUG(1,("INFO: Profiling values cleared from pid %d\n",
68 : (int)procid_to_pid(src)));
69 0 : break;
70 : }
71 0 : }
72 :
73 : /****************************************************************************
74 : receive a set profile level message
75 : ****************************************************************************/
76 0 : static void profile_message(struct messaging_context *msg_ctx,
77 : void *private_data,
78 : uint32_t msg_type,
79 : struct server_id src,
80 : DATA_BLOB *data)
81 : {
82 0 : int level;
83 :
84 0 : if (data->length != sizeof(level)) {
85 0 : DEBUG(0, ("got invalid profile message\n"));
86 0 : return;
87 : }
88 :
89 0 : memcpy(&level, data->data, sizeof(level));
90 0 : set_profile_level(level, &src);
91 : }
92 :
93 : /****************************************************************************
94 : receive a request profile level message
95 : ****************************************************************************/
96 0 : static void reqprofile_message(struct messaging_context *msg_ctx,
97 : void *private_data,
98 : uint32_t msg_type,
99 : struct server_id src,
100 : DATA_BLOB *data)
101 : {
102 0 : int level;
103 :
104 0 : level = 1;
105 0 : if (smbprofile_state.config.do_count) {
106 0 : level += 2;
107 : }
108 0 : if (smbprofile_state.config.do_times) {
109 0 : level += 4;
110 : }
111 :
112 0 : DEBUG(1,("INFO: Received REQ_PROFILELEVEL message from PID %u\n",
113 : (unsigned int)procid_to_pid(&src)));
114 0 : messaging_send_buf(msg_ctx, src, MSG_PROFILELEVEL,
115 : (uint8_t *)&level, sizeof(level));
116 0 : }
117 :
118 : /*******************************************************************
119 : open the profiling shared memory area
120 : ******************************************************************/
121 2 : bool profile_setup(struct messaging_context *msg_ctx, bool rdonly)
122 : {
123 0 : char *db_name;
124 2 : bool ok = false;
125 0 : int rc;
126 :
127 2 : if (smbprofile_state.internal.db != NULL) {
128 0 : return true;
129 : }
130 :
131 2 : db_name = cache_path(talloc_tos(), "smbprofile.tdb");
132 2 : if (db_name == NULL) {
133 0 : return false;
134 : }
135 :
136 2 : smbprofile_state.internal.db = tdb_wrap_open(
137 : NULL, db_name, 0,
138 : rdonly ? 0 : TDB_CLEAR_IF_FIRST|TDB_MUTEX_LOCKING,
139 : O_CREAT | (rdonly ? O_RDONLY : O_RDWR), 0644);
140 2 : TALLOC_FREE(db_name);
141 2 : if (smbprofile_state.internal.db == NULL) {
142 0 : return false;
143 : }
144 :
145 2 : if (msg_ctx != NULL) {
146 0 : messaging_register(msg_ctx, NULL, MSG_PROFILE,
147 : profile_message);
148 0 : messaging_register(msg_ctx, NULL, MSG_REQ_PROFILELEVEL,
149 : reqprofile_message);
150 : }
151 :
152 2 : profile_p = &smbprofile_state.stats.global;
153 :
154 2 : rc = smbprofile_magic(profile_p, &profile_p->magic);
155 2 : if (rc != 0) {
156 0 : goto out;
157 : }
158 :
159 2 : ok = true;
160 2 : out:
161 :
162 2 : return ok;
163 : }
164 :
165 31563 : void smbprofile_dump_setup(struct tevent_context *ev)
166 : {
167 31563 : TALLOC_FREE(smbprofile_state.internal.te);
168 31563 : smbprofile_state.internal.ev = ev;
169 31563 : }
170 :
171 0 : static void smbprofile_dump_timer(struct tevent_context *ev,
172 : struct tevent_timer *te,
173 : struct timeval current_time,
174 : void *private_data)
175 : {
176 0 : smbprofile_dump();
177 0 : }
178 :
179 0 : void smbprofile_dump_schedule_timer(void)
180 : {
181 0 : struct timeval tv;
182 :
183 0 : GetTimeOfDay(&tv);
184 0 : tv.tv_sec += 1;
185 :
186 0 : smbprofile_state.internal.te = tevent_add_timer(
187 : smbprofile_state.internal.ev,
188 : smbprofile_state.internal.ev,
189 : tv,
190 : smbprofile_dump_timer,
191 : NULL);
192 0 : }
193 :
194 0 : static int profile_stats_parser(TDB_DATA key, TDB_DATA value,
195 : void *private_data)
196 : {
197 0 : struct profile_stats *s = private_data;
198 :
199 0 : if (value.dsize != sizeof(struct profile_stats)) {
200 0 : *s = (struct profile_stats) {};
201 0 : return 0;
202 : }
203 :
204 0 : memcpy(s, value.dptr, value.dsize);
205 0 : if (s->magic != profile_p->magic) {
206 0 : *s = (struct profile_stats) {};
207 0 : return 0;
208 : }
209 :
210 0 : return 0;
211 : }
212 :
213 31552 : void smbprofile_dump(void)
214 : {
215 31552 : pid_t pid = 0;
216 31552 : TDB_DATA key = { .dptr = (uint8_t *)&pid, .dsize = sizeof(pid) };
217 31552 : struct profile_stats s = {};
218 842 : int ret;
219 : #ifdef HAVE_GETRUSAGE
220 842 : struct rusage rself;
221 : #endif /* HAVE_GETRUSAGE */
222 :
223 31552 : TALLOC_FREE(smbprofile_state.internal.te);
224 :
225 31552 : if (! (smbprofile_state.config.do_count ||
226 31552 : smbprofile_state.config.do_times)) {
227 30710 : return;
228 : }
229 :
230 0 : if (smbprofile_state.internal.db == NULL) {
231 0 : return;
232 : }
233 :
234 0 : pid = tevent_cached_getpid();
235 :
236 0 : ret = tdb_chainlock(smbprofile_state.internal.db->tdb, key);
237 0 : if (ret != 0) {
238 0 : return;
239 : }
240 :
241 0 : tdb_parse_record(smbprofile_state.internal.db->tdb,
242 : key, profile_stats_parser, &s);
243 :
244 0 : smbprofile_stats_accumulate(profile_p, &s);
245 :
246 : #ifdef HAVE_GETRUSAGE
247 0 : ret = getrusage(RUSAGE_SELF, &rself);
248 0 : if (ret != 0) {
249 0 : ZERO_STRUCT(rself);
250 : }
251 :
252 0 : profile_p->values.cpu_user_stats.time =
253 0 : (rself.ru_utime.tv_sec * 1000000) +
254 0 : rself.ru_utime.tv_usec;
255 0 : profile_p->values.cpu_system_stats.time =
256 0 : (rself.ru_stime.tv_sec * 1000000) +
257 0 : rself.ru_stime.tv_usec;
258 : #endif /* HAVE_GETRUSAGE */
259 :
260 0 : tdb_store(smbprofile_state.internal.db->tdb, key,
261 0 : (TDB_DATA) {
262 : .dptr = (uint8_t *)profile_p,
263 : .dsize = sizeof(*profile_p)
264 : },
265 : 0);
266 :
267 0 : tdb_chainunlock(smbprofile_state.internal.db->tdb, key);
268 0 : ZERO_STRUCT(profile_p->values);
269 :
270 0 : return;
271 : }
272 :
273 0 : void smbprofile_cleanup(pid_t pid, pid_t dst)
274 : {
275 0 : TDB_DATA key = { .dptr = (uint8_t *)&pid, .dsize = sizeof(pid) };
276 0 : struct profile_stats s = {};
277 0 : struct profile_stats acc = {};
278 0 : int ret;
279 :
280 0 : if (smbprofile_state.internal.db == NULL) {
281 0 : return;
282 : }
283 :
284 0 : ret = tdb_chainlock(smbprofile_state.internal.db->tdb, key);
285 0 : if (ret != 0) {
286 0 : return;
287 : }
288 0 : ret = tdb_parse_record(smbprofile_state.internal.db->tdb,
289 : key, profile_stats_parser, &s);
290 0 : if (ret == -1) {
291 0 : tdb_chainunlock(smbprofile_state.internal.db->tdb, key);
292 0 : return;
293 : }
294 0 : tdb_delete(smbprofile_state.internal.db->tdb, key);
295 0 : tdb_chainunlock(smbprofile_state.internal.db->tdb, key);
296 :
297 0 : pid = dst;
298 0 : ret = tdb_chainlock(smbprofile_state.internal.db->tdb, key);
299 0 : if (ret != 0) {
300 0 : return;
301 : }
302 0 : tdb_parse_record(smbprofile_state.internal.db->tdb,
303 : key, profile_stats_parser, &acc);
304 :
305 : /*
306 : * We may have to fix the disconnect count
307 : * in case the process died
308 : */
309 0 : s.values.disconnect_stats.count = s.values.connect_stats.count;
310 :
311 0 : smbprofile_stats_accumulate(&acc, &s);
312 :
313 0 : acc.magic = profile_p->magic;
314 0 : tdb_store(smbprofile_state.internal.db->tdb, key,
315 0 : (TDB_DATA) {
316 : .dptr = (uint8_t *)&acc,
317 : .dsize = sizeof(acc)
318 : },
319 : 0);
320 :
321 0 : tdb_chainunlock(smbprofile_state.internal.db->tdb, key);
322 : }
323 :
324 2 : void smbprofile_collect(struct profile_stats *stats)
325 : {
326 2 : if (smbprofile_state.internal.db == NULL) {
327 0 : return;
328 : }
329 2 : smbprofile_collect_tdb(smbprofile_state.internal.db->tdb,
330 2 : profile_p->magic,
331 : stats);
332 : }
|