packaging: Create directories for building RPM
[amitay/dbench.git] / child.c
1 /* 
2    dbench version 3
3
4    Copyright (C) Andrew Tridgell 1999-2004
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /* This file links against either fileio.c to do operations against a
21    local filesystem (making dbench), or sockio.c to issue SMB-like
22    command packets over a socket (making tbench).
23
24    So, the pattern of operations and the control structure is the same
25    for both benchmarks, but the operations performed are different.
26 */
27
28 #include "dbench.h"
29 #include <zlib.h>
30
31 #define ival(s) strtoll(s, NULL, 0)
32
33 #define RWBUFSIZE 1024*1024
34 char rw_buf[RWBUFSIZE];
35
36 static void nb_sleep(int usec)
37 {
38         usleep(usec);
39 }
40
41
42 static void nb_target_rate(struct child_struct *child, double rate)
43 {
44         double tdelay;
45
46         if (child->rate.last_bytes == 0) {
47                 child->rate.last_bytes = child->bytes;
48                 child->rate.last_time = timeval_current();
49                 return;
50         }
51
52         if (rate != 0) {
53                 tdelay = (child->bytes - child->rate.last_bytes)/(1.0e6*rate) - 
54                         timeval_elapsed(&child->rate.last_time);
55         } else {
56                 tdelay = - timeval_elapsed(&child->rate.last_time);
57         }
58         if (tdelay > 0 && rate != 0) {
59                 msleep(tdelay*1000);
60         } else {
61                 child->max_latency = MAX(child->max_latency, -tdelay);
62         }
63
64         child->rate.last_time = timeval_current();
65         child->rate.last_bytes = child->bytes;
66 }
67
68 static void nb_time_reset(struct child_struct *child)
69 {
70         child->starttime = timeval_current();   
71         memset(&child->rate, 0, sizeof(child->rate));
72 }
73
74 static void nb_time_delay(struct child_struct *child, double targett)
75 {
76         double elapsed = timeval_elapsed(&child->starttime);
77         if (targett > elapsed) {
78                 msleep(1000*(targett - elapsed));
79         } else if (elapsed - targett > child->max_latency) {
80                 child->max_latency = MAX(elapsed - targett, child->max_latency);
81         }
82 }
83
84 static void finish_op(struct child_struct *child, struct op *op)
85 {
86         double t = timeval_elapsed(&child->lasttime);
87         op->count++;
88         op->total_time += t;
89         if (t > op->max_latency) {
90                 op->max_latency = t;
91         }
92 }
93
94 #define OP_LATENCY(opname) finish_op(child, &child->op.op_ ## opname)
95
96 /* here we parse "special" arguments that start with '*'
97  * '*' itself means a random 64 bit number, but this can be qualified as
98  *
99  * '*'     a random 64 bit number
100  * '...%y' modulo y
101  * '.../y' align the number as an integer multiple of y  (( x = (x/y)*y))
102  * '...+y' add 'y'
103  *
104  * Examples :
105  * '*'       : random 64 bit number
106  * '*%1024'  : random number between 0 and 1023
107  * '* /1024'  : random 64 bit number aligned to n*1024
108  * '*%1024/2 : random even number between 0 and 1023
109  *
110  *
111  * a special case is when the format starts with a '+' and is followed by
112  * a number, in which case we reuse the number from the previous line in the
113  * loadfile and add <number> to it :
114  * '+1024' : add 1024 to the value from the previous line in the loadfile
115  */
116 static uint64_t parse_special(const char *fmt, uint64_t prev_val)
117 {
118         char q;
119         uint64_t num;
120         uint64_t val;
121
122         if (*fmt == '+') {
123                 val = strtoll(fmt+1, NULL, 0);
124                 return prev_val + val;
125         }
126
127         num = random();
128         num = (num <<32) | random();
129
130         fmt++;
131         while (*fmt != '\0') {
132                 q = *fmt++;
133                 val = strtoll(fmt, NULL, 0);
134                 if (val == 0) {
135                         printf("Illegal value in random number qualifier. Can not be zero\n");
136                         return num;
137                 }
138
139                 switch (q) {
140                 case '/':
141                         num = (num/val)*val;
142                         break;
143                 case '%':
144                         num = num%val;
145                         break;
146                 case '+':
147                         num = num+val;
148                         break;
149                 default:
150                         printf("Unknown qualifier '%c' for randum number qualifier\n", q);
151                 }
152
153                 /* skip until the next token */
154                 while (*fmt != '\0') {
155                         switch (*fmt) {
156                         case '0'...'9':
157                         case 'a'...'f':
158                         case 'A'...'F':
159                         case 'x':
160                         case 'X':
161                                 fmt++;
162                                 continue;
163                         }
164                         break;
165                 }
166         }
167
168         return num;
169 }
170 /*
171   one child operation
172  */
173 static void child_op(struct child_struct *child, const char *opname,
174                      const char *fname, const char *fname2, 
175                      char **params, const char *status)
176 {
177         static struct dbench_op prev_op;
178         struct dbench_op op;
179         unsigned i;
180
181         child->lasttime = timeval_current();
182
183         ZERO_STRUCT(op);
184         op.child = child;
185         op.op = opname;
186         op.fname = fname;
187         op.fname2 = fname2;
188         op.status = status;
189         for (i=0;i<sizeof(op.params)/sizeof(op.params[0]);i++) {
190                 switch (params[i][0]) {
191                 case '*':
192                 case '+':
193                         op.params[i] = parse_special(params[i], prev_op.params[i]);
194                         break;
195                 default:
196                         op.params[i] = params[i]?ival(params[i]):0;
197                 }
198         }
199
200         prev_op = op;
201
202         if (strcasecmp(op.op, "Sleep") == 0) {
203                 nb_sleep(op.params[0]);
204                 return;
205         }
206
207         for (i=0;nb_ops->ops[i].name;i++) {
208                 if (strcasecmp(op.op, nb_ops->ops[i].name) == 0) {
209                         nb_ops->ops[i].fn(&op);
210                         finish_op(child, &child->ops[i]);
211                         return;
212                 }
213         }
214
215         printf("[%u] Unknown operation %s in pid %u\n", 
216                child->line, op.op, (unsigned)getpid());
217 }
218
219 #define MAX_RND_STR 10
220 static char random_string[MAX_RND_STR][256];
221
222 static int store_random_string(unsigned int idx, char *str)
223 {
224         if (idx >= MAX_RND_STR) {
225                 fprintf(stderr, "'idx' in RANDOMSTRING is too large. %u specified but %u is maximum\n", idx, MAX_RND_STR-1);
226                 return 1;
227         }
228
229
230         strncpy(random_string[idx], str, sizeof(random_string[0]));
231
232         return 0;
233 }
234
235 static char *get_random_string(unsigned int idx)
236 {
237         return random_string[idx];
238 }
239
240 /*
241  * This parses a line of the form :
242  * RANDOMSTRING <idx> <string>
243  * All subpatterns of the form [<characters>] in string are substituted for
244  * a randomly chosen character from the specified set.
245  *
246  * The end result is stored as string index <idx>
247  */
248 static int parse_randomstring(char *line)
249 {
250         int num;
251         char *pstart, *pend, rndc[2];
252         unsigned int idx;
253         char str[256];
254
255 again:
256         pstart = index(line, '[');
257         if (pstart == NULL) {
258                 goto finished;
259         }
260         strncpy(str, pstart, sizeof(str));
261
262         pend = index(str, ']');
263         if (pstart == NULL) {
264                 fprintf(stderr, "Unbalanced '[' in RANDOMSTRING : %s\n", line);
265                 return 1;
266         }
267
268         pend++;
269         *pend = '\0';
270
271         /* pick a random character */
272         num = strlen(str) - 2;
273         rndc[0] = str[random()%num + 1];
274         rndc[1] = '\0';
275
276         single_string_sub(line, str, rndc);
277         goto again;
278
279
280 finished:
281         if (sscanf(line, "RANDOMSTRING %u %s\n", &idx, str) != 2) {
282                 fprintf(stderr, "Invalid RANDOMSTRING line : [%s]\n", line);
283                 return 1;
284         }
285         /* remote initial " */
286         while (str[0] == '"') {
287                 memcpy(str, str+1, sizeof(str)-1);
288         }
289         /* remote trailing " */
290         while (1) {
291                 int len = strlen(str);
292
293                 if (len < 1) {
294                         break;
295                 }
296
297                 if (str[len-1] != '"') {
298                         break;
299                 }
300
301                 str[len-1] = '\0';
302         }
303
304         if (store_random_string(idx, str)) {
305                 fprintf(stderr, "Failed to store randomstring idx:%d str:%s\n", idx, str);
306                 return 1;
307         }
308
309         return 0;
310 }
311
312
313 /* run a test that simulates an approximate netbench client load */
314 #define MAX_PARM_LEN 1024
315 void child_run(struct child_struct *child0, const char *loadfile)
316 {
317         int i;
318         char line[MAX_PARM_LEN], fname[MAX_PARM_LEN], fname2[MAX_PARM_LEN];
319         char **sparams, **params;
320         char *p;
321         const char *status;
322         gzFile *gzf;
323         pid_t parent = getppid();
324         double targett;
325         struct child_struct *child;
326         int have_random = 0;
327         unsigned loop_count = 0;
328         z_off_t loop_start = 0;
329
330         gzf = gzopen(loadfile, "r");
331         if (gzf == NULL) {
332                 perror(loadfile);
333                 exit(1);
334         }
335
336         for (child=child0;child<child0+options.clients_per_process;child++) {
337                 child->line = 0;
338                 asprintf(&child->cname,"client%d", child->id);
339         }
340
341         sparams = calloc(20, sizeof(char *));
342         for (i=0;i<20;i++) {
343                 sparams[i] = malloc(MAX_PARM_LEN);
344                 memset(sparams[i], 0, MAX_PARM_LEN);
345         }
346
347 again:
348         for (child=child0;child<child0+options.clients_per_process;child++) {
349                 nb_time_reset(child);
350         }
351
352         while (gzgets(gzf, line, sizeof(line)-1)) {
353                 unsigned repeat_count = 1;
354
355                 for (child=child0;child<child0+options.clients_per_process;child++) {
356                         if (child->done) goto done;
357                         child->line++;
358                 }
359
360
361                 params = sparams;
362
363                 if (kill(parent, 0) == -1) {
364                         exit(1);
365                 }
366
367 loop_again:
368                 /* if this is a "LOOP <xxx>" line, 
369                  * remember the current file position and move to the next line
370                  */
371                 if (strncmp(line, "LOOP", 4) == 0) {
372                         if (sscanf(line, "LOOP %u\n", &loop_count) != 1) {
373                                 fprintf(stderr, "Incorrect LOOP at line %d\n", child0->line);
374                                 goto done;
375                         }
376
377                         for (child=child0;child<child0+options.clients_per_process;child++) {
378                                 child->line++;
379                         }
380                         loop_start = gztell(gzf);
381                         gzgets(gzf, line, sizeof(line)-1);
382                         goto loop_again;
383                 }
384
385                 if (strncmp(line, "ENDLOOP", 7) == 0) {
386                         loop_count--;
387
388                         gzgets(gzf, line, sizeof(line)-1);
389
390                         if (loop_count > 0) {
391                                 gzseek(gzf, loop_start, SEEK_SET);
392                         }
393                         
394                         gzgets(gzf, line, sizeof(line)-1);
395                         goto loop_again;
396                 }                       
397
398                 /* if this is a "REPEAT <xxx>" line, just replace the
399                  * currently read line with the next line
400                  */
401                 if (strncmp(line, "REPEAT", 6) == 0) {
402                         if (sscanf(line, "REPEAT %u\n", &repeat_count) != 1) {
403                                 fprintf(stderr, "Incorrect REPEAT at line %d\n", child0->line);
404                                 goto done;
405                         }
406
407                         for (child=child0;child<child0+options.clients_per_process;child++) {
408                                 child->line++;
409                         }
410                         gzgets(gzf, line, sizeof(line)-1);
411                 }
412
413
414                 /* WRITEPATTERN */
415                 if (strncmp(line, "WRITEPATTERN", 12) == 0) {
416                         char *ptr = rw_buf;
417                         int count = RWBUFSIZE;
418                         
419                         while (count > 0) {
420                               int len;
421
422                               len = count;
423                               if (len > strlen(line +13)) {
424                                       len = strlen(line +13);
425                               }
426                               memcpy(ptr, line+13, len);
427                               ptr += len;
428                               count -= len;
429                         }
430                         goto again;
431                 }
432
433
434                 /* RANDOMSTRING */
435                 if (strncmp(line, "RANDOMSTRING", 12) == 0) {
436                         have_random = 1;
437                         if (parse_randomstring(line) != 0) {
438                                 fprintf(stderr, "Incorrect RANDOMSTRING at line %d\n", child0->line);
439                                 goto done;
440                         }
441                         goto again;
442                 }
443
444
445                 line[strlen(line)-1] = 0;
446
447                 all_string_sub(line,"\\", "/");
448                 all_string_sub(line," /", " ");
449
450                 /* substitute all $<digit> stored strings */
451                 while (have_random && (p = index(line, '$')) != NULL) {
452                         char sstr[3], *nstr;
453                         unsigned int idx;
454                       
455                         idx = *(p+1) - '0';
456                         if (idx >= MAX_RND_STR) {
457                                 fprintf(stderr, "$%d is an invalid filename/string\n", idx);
458                                 goto done;
459                         }
460
461                         sstr[0] = '$';
462                         sstr[1] = idx+'0';
463                         sstr[2] = '\0';
464
465                         nstr = get_random_string(idx);
466                         all_string_sub(line, sstr, nstr);
467                 }
468                 
469                 p = line;
470                 for (i=0; 
471                      i<19 && next_token(&p, params[i], " ");
472                      i++) ;
473
474                 params[i][0] = 0;
475
476                 if (i < 2 || params[0][0] == '#') continue;
477
478                 if (!strncmp(params[0],"SMB", 3)) {
479                         printf("ERROR: You are using a dbench 1 load file\n");
480                         exit(1);
481                 }
482
483                 if (i > 0 && isdigit(params[0][0])) {
484                         targett = strtod(params[0], NULL);
485                         params++;
486                         i--;
487                 } else {
488                         targett = 0.0;
489                 }
490
491                 if (strncmp(params[i-1], "NT_STATUS_", 10) != 0 &&
492                     strncmp(params[i-1], "0x", 2) != 0 &&
493                     strncmp(params[i-1], "SUCCESS", 7) != 0 &&
494                     strncmp(params[i-1], "ERROR", 7) != 0 &&
495                     strncmp(params[i-1], "*", 1) != 0) {
496                         printf("Badly formed status at line %d\n", child->line);
497                         continue;
498                 }
499
500                 status = params[i-1];
501
502                 
503                 for (child=child0;child<child0+options.clients_per_process;child++) {
504                         unsigned child_repeat_count = repeat_count;
505                         int pcount = 1;
506
507                         fname[0] = 0;
508                         fname2[0] = 0;
509
510                         if (i>1 && params[1][0] == '/') {
511                                 snprintf(fname, sizeof(fname), "%s%s", child->directory, params[1]);
512                                 all_string_sub(fname,"client1", child->cname);
513                                 pcount++;
514                         }
515                         if (i>2 && params[2][0] == '/') {
516                                 snprintf(fname2, sizeof(fname2), "%s%s", child->directory, params[2]);
517                                 all_string_sub(fname2,"client1", child->cname);
518                                 pcount++;
519                         }
520
521                         if (options.targetrate != 0 || targett == 0.0) {
522                                 nb_target_rate(child, options.targetrate);
523                         } else {
524                                 nb_time_delay(child, targett);
525                         }
526                         while (child_repeat_count--) {
527                                 child_op(child, params[0], fname, fname2, params+pcount, status);
528                         }
529                 }
530         }
531
532         if (options.run_once) {
533                 goto done;
534         }
535
536         gzrewind(gzf);
537         goto again;
538
539 done:
540         gzclose(gzf);
541         for (child=child0;child<child0+options.clients_per_process;child++) {
542                 child->cleanup = 1;
543                 fflush(stdout);
544                 if (!options.skip_cleanup) {
545                         nb_ops->cleanup(child);
546                 }
547                 child->cleanup_finished = 1;
548                 if(child->cname){
549                         free(child->cname);
550                         child->cname = NULL;
551                 }
552         }
553 }