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 : if (smbprofile_state.internal.db == NULL) {
141 0 : return false;
142 : }
143 :
144 2 : if (msg_ctx != NULL) {
145 0 : messaging_register(msg_ctx, NULL, MSG_PROFILE,
146 : profile_message);
147 0 : messaging_register(msg_ctx, NULL, MSG_REQ_PROFILELEVEL,
148 : reqprofile_message);
149 : }
150 :
151 2 : profile_p = &smbprofile_state.stats.global;
152 :
153 2 : rc = smbprofile_magic(profile_p, &profile_p->magic);
154 2 : if (rc != 0) {
155 0 : goto out;
156 : }
157 :
158 2 : ok = true;
159 2 : out:
160 :
161 2 : return ok;
162 : }
163 :
164 31051 : void smbprofile_dump_setup(struct tevent_context *ev)
165 : {
166 31051 : TALLOC_FREE(smbprofile_state.internal.te);
167 31051 : smbprofile_state.internal.ev = ev;
168 31051 : }
169 :
170 0 : static void smbprofile_dump_timer(struct tevent_context *ev,
171 : struct tevent_timer *te,
172 : struct timeval current_time,
173 : void *private_data)
174 : {
175 0 : smbprofile_dump();
176 0 : }
177 :
178 0 : void smbprofile_dump_schedule_timer(void)
179 : {
180 0 : struct timeval tv;
181 :
182 0 : GetTimeOfDay(&tv);
183 0 : tv.tv_sec += 1;
184 :
185 0 : smbprofile_state.internal.te = tevent_add_timer(
186 : smbprofile_state.internal.ev,
187 : smbprofile_state.internal.ev,
188 : tv,
189 : smbprofile_dump_timer,
190 : NULL);
191 0 : }
192 :
193 0 : static int profile_stats_parser(TDB_DATA key, TDB_DATA value,
194 : void *private_data)
195 : {
196 0 : struct profile_stats *s = private_data;
197 :
198 0 : if (value.dsize != sizeof(struct profile_stats)) {
199 0 : *s = (struct profile_stats) {};
200 0 : return 0;
201 : }
202 :
203 0 : memcpy(s, value.dptr, value.dsize);
204 0 : if (s->magic != profile_p->magic) {
205 0 : *s = (struct profile_stats) {};
206 0 : return 0;
207 : }
208 :
209 0 : return 0;
210 : }
211 :
212 31040 : void smbprofile_dump(void)
213 : {
214 31040 : pid_t pid = 0;
215 31040 : TDB_DATA key = { .dptr = (uint8_t *)&pid, .dsize = sizeof(pid) };
216 31040 : struct profile_stats s = {};
217 842 : int ret;
218 : #ifdef HAVE_GETRUSAGE
219 842 : struct rusage rself;
220 : #endif /* HAVE_GETRUSAGE */
221 :
222 31040 : TALLOC_FREE(smbprofile_state.internal.te);
223 :
224 31040 : if (! (smbprofile_state.config.do_count ||
225 31040 : smbprofile_state.config.do_times)) {
226 30198 : return;
227 : }
228 :
229 0 : if (smbprofile_state.internal.db == NULL) {
230 0 : return;
231 : }
232 :
233 0 : pid = tevent_cached_getpid();
234 :
235 : #ifdef HAVE_GETRUSAGE
236 0 : ret = getrusage(RUSAGE_SELF, &rself);
237 0 : if (ret != 0) {
238 0 : ZERO_STRUCT(rself);
239 : }
240 :
241 0 : profile_p->values.cpu_user_stats.time =
242 0 : (rself.ru_utime.tv_sec * 1000000) +
243 0 : rself.ru_utime.tv_usec;
244 0 : profile_p->values.cpu_system_stats.time =
245 0 : (rself.ru_stime.tv_sec * 1000000) +
246 0 : rself.ru_stime.tv_usec;
247 : #endif /* HAVE_GETRUSAGE */
248 :
249 0 : ret = tdb_chainlock(smbprofile_state.internal.db->tdb, key);
250 0 : if (ret != 0) {
251 0 : return;
252 : }
253 :
254 0 : tdb_parse_record(smbprofile_state.internal.db->tdb,
255 : key, profile_stats_parser, &s);
256 :
257 0 : smbprofile_stats_accumulate(profile_p, &s);
258 :
259 0 : tdb_store(smbprofile_state.internal.db->tdb, key,
260 0 : (TDB_DATA) {
261 : .dptr = (uint8_t *)profile_p,
262 : .dsize = sizeof(*profile_p)
263 : },
264 : 0);
265 :
266 0 : tdb_chainunlock(smbprofile_state.internal.db->tdb, key);
267 0 : ZERO_STRUCT(profile_p->values);
268 :
269 0 : return;
270 : }
271 :
272 0 : void smbprofile_cleanup(pid_t pid, pid_t dst)
273 : {
274 0 : TDB_DATA key = { .dptr = (uint8_t *)&pid, .dsize = sizeof(pid) };
275 0 : struct profile_stats s = {};
276 0 : struct profile_stats acc = {};
277 0 : int ret;
278 :
279 0 : if (smbprofile_state.internal.db == NULL) {
280 0 : return;
281 : }
282 :
283 0 : ret = tdb_chainlock(smbprofile_state.internal.db->tdb, key);
284 0 : if (ret != 0) {
285 0 : return;
286 : }
287 0 : ret = tdb_parse_record(smbprofile_state.internal.db->tdb,
288 : key, profile_stats_parser, &s);
289 0 : if (ret == -1) {
290 0 : tdb_chainunlock(smbprofile_state.internal.db->tdb, key);
291 0 : return;
292 : }
293 0 : tdb_delete(smbprofile_state.internal.db->tdb, key);
294 0 : tdb_chainunlock(smbprofile_state.internal.db->tdb, key);
295 :
296 0 : pid = dst;
297 0 : ret = tdb_chainlock(smbprofile_state.internal.db->tdb, key);
298 0 : if (ret != 0) {
299 0 : return;
300 : }
301 0 : tdb_parse_record(smbprofile_state.internal.db->tdb,
302 : key, profile_stats_parser, &acc);
303 :
304 : /*
305 : * We may have to fix the disconnect count
306 : * in case the process died
307 : */
308 0 : s.values.disconnect_stats.count = s.values.connect_stats.count;
309 :
310 0 : smbprofile_stats_accumulate(&acc, &s);
311 :
312 0 : acc.magic = profile_p->magic;
313 0 : tdb_store(smbprofile_state.internal.db->tdb, key,
314 0 : (TDB_DATA) {
315 : .dptr = (uint8_t *)&acc,
316 : .dsize = sizeof(acc)
317 : },
318 : 0);
319 :
320 0 : tdb_chainunlock(smbprofile_state.internal.db->tdb, key);
321 : }
322 :
323 2 : void smbprofile_collect(struct profile_stats *stats)
324 : {
325 2 : if (smbprofile_state.internal.db == NULL) {
326 0 : return;
327 : }
328 2 : smbprofile_collect_tdb(smbprofile_state.internal.db->tdb,
329 2 : profile_p->magic,
330 : stats);
331 : }
|