Fix bad use when freeing linked list. Todd Stecher (Original author) please check !
[kai/samba.git] / source3 / modules / perfcount_test.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Test module for perfcounters
4  *
5  * Copyright (C) Todd Stecher 2008
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 #include "includes.h"
22
23 #define PARM_PC_TEST_TYPE               "pc_test"
24 #define PARM_DUMPON_COUNT               "count"
25 #define PARM_DUMPON_COUNT_DEFAULT       50
26
27 struct perfcount_test_identity {
28         uid_t uid;
29         char *user;
30         char *domain;
31 };
32
33 struct perfcount_test_counter {
34         int op;
35         int sub_op;
36         int ioctl;
37         uint64_t bytes_in;
38         uint64_t bytes_out;
39         int count;
40
41         struct perfcount_test_counter *next;
42         struct perfcount_test_counter *prev;
43 };
44
45 struct perfcount_test_context {
46
47         /* wip:  identity */
48         struct perfcount_test_identity *id;
49         struct perfcount_test_counter *ops;
50 };
51
52 #define MAX_OP 256
53 struct perfcount_test_counter *g_list[MAX_OP];
54
55 int count;
56
57 /* determine frequency of dumping results */
58 int count_mod = 1;
59
60 static void perfcount_test_add_counters(struct perfcount_test_context *ctxt)
61 {
62         struct perfcount_test_counter *head;
63         struct perfcount_test_counter *ptc;
64         struct perfcount_test_counter *tmp;
65         bool found;
66
67         for (ptc = ctxt->ops; ptc != NULL; ) {
68
69                 found = false;
70
71                 if (ptc->op > MAX_OP)
72                         continue;
73
74                 for (head = g_list[ptc->op]; head != NULL; head = head->next) {
75                         if ((ptc->sub_op == head->sub_op) &&
76                             (ptc->ioctl == head->ioctl)) {
77                                 head->bytes_in += ptc->bytes_in;
78                                 head->bytes_out += ptc->bytes_out;
79                                 head->count++;
80                                 tmp = ptc->next;
81                                 DLIST_REMOVE(ctxt->ops, ptc);
82                                 SAFE_FREE(ptc);
83                                 ptc = tmp;
84                                 found = true;
85                                 break;
86                         }
87                 }
88
89                 /* not in global tracking list - add it */
90                 if (!found) {
91                         tmp = ptc->next;
92                         DLIST_REMOVE(ctxt->ops, ptc);
93                         ptc->count = 1;
94                         DLIST_ADD(g_list[ptc->op], ptc);
95                         ptc = tmp;
96                 }
97         }
98
99 }
100
101 #if 0
102
103 static void perfcount_test_dump_id(struct perfcount_test_identity *id, int lvl)
104 {
105         if (!id)
106                 return;
107
108         DEBUG(lvl,("uid - %d\n", id->uid));
109         DEBUG(lvl,("user - %s\n", id->user));
110         DEBUG(lvl,("domain - %s\n", id->domain));
111 }
112
113 #endif
114
115 static const char *trans_subop_table[] = {
116         "unknown", "trans:create", "trans:ioctl", "trans:set sd",
117         "trans:change notify", "trans: rename", "trans:get sd",
118         "trans:get quota", "trans:set quota"
119 };
120
121 static const char *trans2_subop_table[] = {
122         "trans2:open", "trans2:find first", "trans2:find next",
123         "trans2:q fsinfo", "trans2:set fsinfo", "trans2:q path info",
124         "trans2:set pathinfo", "trans2:fs ctl", "trans2: io ctl",
125         "trans2:find notify first", "trans2:find notify next",
126         "trans2:mkdir", "trans2:sess setup", "trans2:get dfs referral",
127         "trans2:report dfs inconsistent"
128 };
129
130 static const char *smb_subop_name(int op, int subop)
131 {
132         /* trans */
133         if (op == 0x25) {
134                 if (subop > sizeof(trans_subop_table) /
135                     sizeof(trans_subop_table[0])) {
136                         return "unknown";
137                 }
138                 return trans_subop_table[subop];
139         } else if (op == 0x32) {
140                 if (subop > sizeof(trans2_subop_table) /
141                     sizeof(trans2_subop_table[0])) {
142                         return "unknown";
143                 }
144                 return trans2_subop_table[subop];
145         }
146
147         return "unknown";
148 }
149
150 static void perfcount_test_dump_counter(struct perfcount_test_counter *ptc,
151                                         int lvl)
152 {
153         DEBUG(lvl, ("OP: %s\n", smb_fn_name(ptc->op)));
154         if (ptc->sub_op > 0) {
155                 DEBUG(lvl, ("SUBOP: %s\n",
156                         smb_subop_name(ptc->op, ptc->sub_op)));
157         }
158
159         if (ptc->ioctl > 0) {
160                 DEBUG(lvl, ("IOCTL: %d\n", ptc->ioctl));
161         }
162
163         DEBUG(lvl, ("Count: %d\n\n", ptc->count));
164 }
165
166 static void perfcount_test_dump_counters(void)
167 {
168         int i;
169         struct perfcount_test_counter *head;
170
171         count_mod = lp_parm_int(0, PARM_PC_TEST_TYPE, PARM_DUMPON_COUNT,
172             PARM_DUMPON_COUNT_DEFAULT);
173
174         if ((count++ % count_mod) != 0)
175                 return;
176
177         DEBUG(0,("#####  Dumping Performance Counters #####\n"));
178
179         for (i=0; i < MAX_OP; i++) {
180                 struct perfcount_test_counter *next;
181                 for (head = g_list[i]; head != NULL; head = next) {
182                         next = head->next;
183                         perfcount_test_dump_counter(head, 0);
184                         SAFE_FREE(head);
185                 }
186                 g_list[i] = NULL;
187         }
188 }
189
190 /*  operations */
191 static void perfcount_test_start(struct smb_perfcount_data *pcd)
192 {
193         struct perfcount_test_context *ctxt;
194         struct perfcount_test_counter *ctr;
195         /*
196          * there shouldn't already be a context here - if so,
197          * there's an unbalanced call to start / end.
198          */
199         if (pcd->context) {
200                 DEBUG(0,("perfcount_test_start - starting "
201                          "initialized context - %p\n", pcd));
202                 return;
203         }
204
205         ctxt = SMB_MALLOC_P(struct perfcount_test_context);
206         if (!ctxt)
207                 return;
208
209         ZERO_STRUCTP(ctxt);
210
211         /* create 'default' context */
212         ctr = SMB_MALLOC_P(struct perfcount_test_counter);
213         if (!ctr) {
214                 SAFE_FREE(ctxt);
215                 return;
216         }
217
218         ZERO_STRUCTP(ctr);
219         ctr->op = ctr->sub_op = ctr->ioctl = -1;
220         DLIST_ADD(ctxt->ops, ctr);
221
222         pcd->context = (void*)ctxt;
223 }
224
225 static void perfcount_test_add(struct smb_perfcount_data *pcd)
226 {
227         struct perfcount_test_context *ctxt =
228                 (struct perfcount_test_context *)pcd->context;
229         struct perfcount_test_counter *ctr;
230
231         if (pcd->context == NULL)
232                 return;
233
234         ctr = SMB_MALLOC_P(struct perfcount_test_counter);
235         if (!ctr) {
236                 return;
237         }
238
239         DLIST_ADD(ctxt->ops, ctr);
240
241 }
242
243 static void perfcount_test_set_op(struct smb_perfcount_data *pcd, int op)
244 {
245         struct perfcount_test_context *ctxt =
246                 (struct perfcount_test_context *)pcd->context;
247
248         if (pcd->context == NULL)
249                 return;
250
251         ctxt->ops->op = op;
252 }
253
254 static void perfcount_test_set_subop(struct smb_perfcount_data *pcd, int sub_op)
255 {
256         struct perfcount_test_context *ctxt =
257                 (struct perfcount_test_context *)pcd->context;
258
259         if (pcd->context == NULL)
260                 return;
261
262         ctxt->ops->sub_op = sub_op;
263 }
264
265 static void perfcount_test_set_ioctl(struct smb_perfcount_data *pcd, int io_ctl)
266 {
267         struct perfcount_test_context *ctxt =
268                 (struct perfcount_test_context *)pcd->context;
269         if (pcd->context == NULL)
270                 return;
271
272         ctxt->ops->ioctl = io_ctl;
273 }
274
275 static void perfcount_test_set_msglen_in(struct smb_perfcount_data *pcd,
276                                          uint64_t bytes_in)
277 {
278         struct perfcount_test_context *ctxt =
279                 (struct perfcount_test_context *)pcd->context;
280         if (pcd->context == NULL)
281                 return;
282
283         ctxt->ops->bytes_in = bytes_in;
284 }
285
286 static void perfcount_test_set_msglen_out(struct smb_perfcount_data *pcd,
287                                           uint64_t bytes_out)
288 {
289         struct perfcount_test_context *ctxt =
290                 (struct perfcount_test_context *)pcd->context;
291
292         if (pcd->context == NULL)
293                 return;
294
295         ctxt->ops->bytes_out = bytes_out;
296 }
297
298 static void perfcount_test_copy_context(struct smb_perfcount_data *pcd,
299                                         struct smb_perfcount_data *new_pcd)
300 {
301         struct perfcount_test_context *ctxt =
302                 (struct perfcount_test_context *)pcd->context;
303         struct perfcount_test_context *new_ctxt;
304
305         struct perfcount_test_counter *ctr;
306         struct perfcount_test_counter *new_ctr;
307
308         if (pcd->context == NULL)
309                 return;
310
311         new_ctxt = SMB_MALLOC_P(struct perfcount_test_context);
312         if (!new_ctxt) {
313                 return;
314         }
315
316         memcpy(new_ctxt, ctxt, sizeof(struct perfcount_test_context));
317
318         for (ctr = ctxt->ops; ctr != NULL; ctr = ctr->next) {
319                 new_ctr = SMB_MALLOC_P(struct perfcount_test_counter);
320                 if (!new_ctr) {
321                         goto error;
322                 }
323
324                 memcpy(new_ctr, ctr, sizeof(struct perfcount_test_counter));
325                 new_ctr->next = NULL;
326                 new_ctr->prev = NULL;
327                 DLIST_ADD(new_ctxt->ops, new_ctr);
328         }
329
330         new_pcd->context = new_ctxt;
331         return;
332
333 error:
334
335         for (ctr = new_ctxt->ops; ctr != NULL; ) {
336                 new_ctr = ctr->next;
337                 SAFE_FREE(ctr);
338                 ctr = new_ctr;
339         }
340
341         SAFE_FREE(new_ctxt);
342 }
343
344 /*
345  * For perf reasons, its best to use some global state
346  * when an operation is deferred, we need to alloc a copy.
347  */
348 static void perfcount_test_defer_op(struct smb_perfcount_data *pcd,
349                                     struct smb_perfcount_data *def_pcd)
350 {
351         /* we don't do anything special to deferred ops */
352         return;
353 }
354
355 static void perfcount_test_end(struct smb_perfcount_data *pcd)
356 {
357         struct perfcount_test_context *ctxt =
358                 (struct perfcount_test_context *)pcd->context;
359         if (pcd->context == NULL)
360                 return;
361
362         /* @bug - we don't store outbytes right for chained cmds */
363         perfcount_test_add_counters(ctxt);
364         perfcount_test_dump_counters();
365         pcd->context = NULL;
366         SAFE_FREE(ctxt);
367 }
368
369
370 static struct smb_perfcount_handlers perfcount_test_handlers = {
371         perfcount_test_start,
372         perfcount_test_add,
373         perfcount_test_set_op,
374         perfcount_test_set_subop,
375         perfcount_test_set_ioctl,
376         perfcount_test_set_msglen_in,
377         perfcount_test_set_msglen_out,
378         perfcount_test_copy_context,
379         perfcount_test_defer_op,
380         perfcount_test_end
381 };
382
383 NTSTATUS perfcount_test_init(void)
384 {
385         return smb_register_perfcounter(SMB_PERFCOUNTER_INTERFACE_VERSION,
386                                         "pc_test", &perfcount_test_handlers);
387 }