vinyl-cache/bin/vinyltest/vtc_logexp.c
0
/*-
1
 * Copyright (c) 2008-2015 Varnish Software AS
2
 * All rights reserved.
3
 *
4
 * Author: Martin Blix Grydeland <martin@varnish-software.com>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29
30
#ifdef VTEST_WITH_VTC_LOGEXPECT
31
32
/* SECTION: logexpect logexpect
33
 *
34
 * Reads the VSL and looks for records matching a given specification. It will
35
 * process records trying to match the first pattern, and when done, will
36
 * continue processing, trying to match the following pattern. If a pattern
37
 * isn't matched, the test will fail.
38
 *
39
 * logexpect threads are declared this way::
40
 *
41
 *         logexpect lNAME -v <id> [-g <grouping>] [-d 0|1] [-q query] \
42
 *                 [vsl arguments] {
43
 *                         expect <skip> <vxid> <tag> <regex>
44
 *                         expect <skip> <vxid> <tag> <regex>
45
 *                         fail add <vxid> <tag> <regex>
46
 *                         fail clear
47
 *                         abort
48
 *                         ...
49
 *                 } [-start|-wait|-run]
50
 *
51
 * And once declared, you can start them, or wait on them::
52
 *
53
 *         logexpect lNAME <-start|-wait>
54
 *
55
 * With:
56
 *
57
 * lNAME
58
 *         Name the logexpect thread, it must start with 'l'.
59
 *
60
 * \-v id
61
 *         Specify the vinyl instance to use (most of the time, id=v1).
62
 *
63
 * \-g <session|request|vxid|raw
64
 *         Decide how records are grouped, see -g in ``man vinyllog`` for more
65
 *         information.
66
 *
67
 * \-d <0|1>
68
 *         Start processing log records at the head of the log instead of the
69
 *         tail.
70
 *
71
 * \-q query
72
 *         Filter records using a query expression, see ``man vsl-query`` for
73
 *         more information. Multiple -q options are not supported.
74
 *
75
 * \-m
76
 *         Also emit log records for misses (only for debugging)
77
 *
78
 * \-err
79
 *         Invert the meaning of success. Usually called once to expect the
80
 *         logexpect to fail
81
 *
82
 * \-start
83
 *         Start the logexpect thread in the background.
84
 *
85
 * \-wait
86
 *         Wait for the logexpect thread to finish
87
 *
88
 * \-run
89
 *        Equivalent to "-start -wait".
90
 *
91
 * VSL arguments (similar to the vinyllog options):
92
 *
93
 * \-C
94
 *         Use caseless regex
95
 *
96
 * \-i <taglist>
97
 *         Include tags
98
 *
99
 * \-I <[taglist:]regex>
100
 *         Include by regex
101
 *
102
 * \-T <seconds>
103
 *         Transaction end timeout
104
 *
105
 * expect specification:
106
 *
107
 * skip: [uint|*|?]
108
 *         Max number of record to skip
109
 *
110
 * vxid: [uint|*|=]
111
 *         vxid to match
112
 *
113
 * tag:  [tagname|*|=]
114
 *         Tag to match against
115
 *
116
 * regex:
117
 *         regular expression to match against (optional)
118
 *
119
 * For skip, vxid and tag, '*' matches anything, '=' expects the value of the
120
 * previous matched record. The '?' marker is equivalent to zero, expecting a
121
 * match on the next record. The difference is that '?' can be used when the
122
 * order of individual consecutive logs is not deterministic. In other words,
123
 * lines from a block of alternatives marked by '?' can be matched in any order,
124
 * but all need to match eventually.
125
 *
126
 * fail specification:
127
 *
128
 * add: Add to the fail list
129
 *
130
 *      Arguments are equivalent to expect, except for skip missing
131
 *
132
 * clear: Clear the fail list
133
 *
134
 * Any number of fail specifications can be active during execution of
135
 * a logexpect. All active fail specifications are matched against every
136
 * log line and, if any match, the logexpect fails immediately.
137
 *
138
 * For a logexpect to end successfully, there must be no specs on the fail list,
139
 * so logexpects should always end with
140
 *
141
 *      expect <skip> <vxid> <tag> <termination-condition>
142
 *      fail clear
143
 *
144
 * .. XXX can we come up with a better solution which is still safe?
145
 *
146
 * abort specification:
147
 *
148
 * abort(3) vinyltest, intended to help debugging of the VSL client library
149
 * itself.
150
 */
151
152
#include "config.h"
153
154
#include <pthread.h>
155
#include <signal.h>
156
#include <stdint.h>
157
#include <stdio.h>
158
#include <stdlib.h>
159
#include <string.h>
160
161
#include "vapi/vsm.h"
162
#include "vapi/vsl.h"
163
164
#include "vdef.h"
165
166
#include "vas.h"
167
#include "miniobj.h"
168
#include "vqueue.h"
169
#include "vre.h"
170
#include "vsb.h"
171
#include "vtim.h"
172
173
#include <vtest_api.h>
174
#include "vtest_ext_vinyl.h"
175
176
#define LE_ANY   (-1)
177
#define LE_LAST  (-2)
178
#define LE_ALT   (-3)
179
#define LE_SEEN  (-4)
180
#define LE_FAIL  (-5)
181
#define LE_CLEAR (-6)   // clear fail list
182
#define LE_ABORT (-7)
183
184
struct logexp_test {
185
        unsigned                        magic;
186
#define LOGEXP_TEST_MAGIC               0x6F62B350
187
        VTAILQ_ENTRY(logexp_test)       list;
188
        VTAILQ_ENTRY(logexp_test)       faillist;
189
190
        struct vsb                      *str;
191
        int64_t                         vxid;
192
        int                             tag;
193
        vre_t                           *vre;
194
        int                             skip_max;
195
};
196
197
VTAILQ_HEAD(tests_head,logexp_test);
198
199
struct logexp {
200
        unsigned                        magic;
201
#define LOGEXP_MAGIC                    0xE81D9F1B
202
        VTAILQ_ENTRY(logexp)            list;
203
204
        char                            *name;
205
        char                            *vname;
206
        struct vtclog                   *vl;
207
        char                            run;
208
        struct tests_head               tests;
209
210
        struct logexp_test              *test;
211
        int                             skip_cnt;
212
        int64_t                         vxid_last;
213
        int                             tag_last;
214
215
        struct tests_head               fail;
216
217
        int                             m_arg;
218
        int                             err_arg;
219
        int                             d_arg;
220
        enum VSL_grouping_e             g_arg;
221
        char                            *query;
222
223
        struct vsm                      *vsm;
224
        struct VSL_data                 *vsl;
225
        struct VSLQ                     *vslq;
226
        pthread_t                       tp;
227
};
228
229
static VTAILQ_HEAD(, logexp)            logexps =
230
        VTAILQ_HEAD_INITIALIZER(logexps);
231
232
static cmd_f cmd_logexp_expect;
233
static cmd_f cmd_logexp_fail;
234
static cmd_f cmd_logexp_abort;
235
236
static const struct cmds logexp_cmds[] = {
237
        { CMDS_MAGIC, "expect",         cmd_logexp_expect },
238
        { CMDS_MAGIC, "fail",           cmd_logexp_fail },
239
        { CMDS_MAGIC, "abort",          cmd_logexp_abort },
240
        { CMDS_MAGIC, NULL,             NULL },
241
};
242
243
static void
244 13671
logexp_delete_tests(struct logexp *le)
245
{
246
        struct logexp_test *test;
247
248 13671
        CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC);
249 58002
        while (!VTAILQ_EMPTY(&le->tests)) {
250 44331
                test = VTAILQ_FIRST(&le->tests);
251 44331
                CHECK_OBJ_NOTNULL(test, LOGEXP_TEST_MAGIC);
252 44331
                VTAILQ_REMOVE(&le->tests, test, list);
253 44331
                VSB_destroy(&test->str);
254 44331
                if (test->vre)
255 36750
                        VRE_free(&test->vre);
256 44331
                FREE_OBJ(test);
257
        }
258 13671
}
259
260
static void
261 6237
logexp_delete(struct logexp *le)
262
{
263 6237
        CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC);
264 6237
        AZ(le->run);
265 6237
        AN(le->vsl);
266 6237
        VSL_Delete(le->vsl);
267 6237
        AZ(le->vslq);
268 6237
        logexp_delete_tests(le);
269 6237
        free(le->name);
270 6237
        free(le->vname);
271 6237
        free(le->query);
272 6237
        VSM_Destroy(&le->vsm);
273 6237
        vtc_logclose(le->vl);
274 6237
        FREE_OBJ(le);
275 6237
}
276
277
static struct logexp *
278 6237
logexp_new(const char *name, const char *varg)
279
{
280
        struct logexp *le;
281
        struct vsb *n_arg;
282
283 6237
        ALLOC_OBJ(le, LOGEXP_MAGIC);
284 6237
        AN(le);
285 6237
        REPLACE(le->name, name);
286 6237
        le->vl = vtc_logopen("%s", name);
287 6237
        vtc_log_set_cmd(le->vl, logexp_cmds);
288 6237
        VTAILQ_INIT(&le->tests);
289
290 6237
        le->d_arg = 0;
291 6237
        le->g_arg = VSL_g_vxid;
292 6237
        le->vsm = VSM_New();
293 6237
        le->vsl = VSL_New();
294 6237
        AN(le->vsm);
295 6237
        AN(le->vsl);
296
297 6237
        VTAILQ_INSERT_TAIL(&logexps, le, list);
298
299 6237
        REPLACE(le->vname, varg);
300
301 6237
        n_arg = macro_expandf(le->vl, "${tmpdir}/%s", varg);
302 6237
        if (n_arg == NULL)
303 0
                vtc_fatal(le->vl, "-v argument problems");
304 6237
        if (VSM_Arg(le->vsm, 'n', VSB_data(n_arg)) <= 0)
305 0
                vtc_fatal(le->vl, "-v argument error: %s",
306 0
                    VSM_Error(le->vsm));
307 6237
        VSB_destroy(&n_arg);
308 6237
        if (VSM_Attach(le->vsm, -1))
309 0
                vtc_fatal(le->vl, "VSM_Attach: %s", VSM_Error(le->vsm));
310 6237
        return (le);
311
}
312
313
static void
314 7518
logexp_clean(const struct tests_head *head)
315
{
316
        struct logexp_test *test;
317
318 51933
        VTAILQ_FOREACH(test, head, list)
319 44415
                if (test->skip_max == LE_SEEN)
320 0
                        test->skip_max = LE_ALT;
321 7518
}
322
323
static struct logexp_test *
324 105
logexp_alt(struct logexp_test *test)
325
{
326 105
        assert(test->skip_max == LE_ALT);
327
328 105
        do
329 231
                test = VTAILQ_NEXT(test, list);
330 126
        while (test != NULL && test->skip_max == LE_SEEN);
331
332 105
        if (test == NULL || test->skip_max != LE_ALT)
333 0
                return (NULL);
334
335 105
        return (test);
336 105
}
337
338
static void
339 51996
logexp_next(struct logexp *le)
340
{
341 51996
        CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC);
342
343 51996
        if (le->test && le->test->skip_max == LE_ALT) {
344
                /*
345
                 * if an alternative was not seen, continue at this expection
346
                 * with the next vsl
347
                 */
348
                (void)0;
349 51996
        } else if (le->test) {
350 44415
                CHECK_OBJ_NOTNULL(le->test, LOGEXP_TEST_MAGIC);
351 44415
                le->test = VTAILQ_NEXT(le->test, list);
352 44415
        } else {
353 7518
                logexp_clean(&le->tests);
354 7518
                VTAILQ_INIT(&le->fail);
355 7518
                le->test = VTAILQ_FIRST(&le->tests);
356
        }
357
358 51996
        if (le->test == NULL)
359 7518
                return;
360
361 44478
        CHECK_OBJ(le->test, LOGEXP_TEST_MAGIC);
362
363 44478
        switch (le->test->skip_max) {
364
        case LE_SEEN:
365 63
                logexp_next(le);
366 63
                return;
367
        case LE_CLEAR:
368 2079
                vtc_log(le->vl, 3, "cond | fail clear");
369 2079
                VTAILQ_INIT(&le->fail);
370 2079
                logexp_next(le);
371 2079
                return;
372
        case LE_FAIL:
373 2373
                vtc_log(le->vl, 3, "cond | %s", VSB_data(le->test->str));
374 2373
                VTAILQ_INSERT_TAIL(&le->fail, le->test, faillist);
375 2373
                logexp_next(le);
376 2373
                return;
377
        case LE_ABORT:
378 0
                abort();
379
                NEEDLESS(return);
380
        default:
381 39963
                vtc_log(le->vl, 3, "test | %s", VSB_data(le->test->str));
382 39963
        }
383 51996
}
384
385
enum le_match_e {
386
        LEM_OK,
387
        LEM_SKIP,
388
        LEM_FAIL
389
};
390
391
static enum le_match_e
392 741654
logexp_match(const struct logexp *le, struct logexp_test *test,
393
    const char *data, int64_t vxid, int tag, int type, int len)
394
{
395
        const char *legend;
396 741654
        int ok = 1, skip = 0, alt, fail, vxid_ok = 0;
397
398 741654
        AN(le);
399 741654
        AN(test);
400 741654
        assert(test->skip_max != LE_SEEN);
401 741654
        assert(test->skip_max != LE_CLEAR);
402
403 741654
        if (test->vxid == LE_LAST) {
404 106251
                if (le->vxid_last != vxid)
405 7480
                        ok = 0;
406 106251
                vxid_ok = ok;
407 741654
        } else if (test->vxid >= 0) {
408 197471
                if (test->vxid != vxid)
409 136029
                        ok = 0;
410 197471
                vxid_ok = ok;
411 197471
        }
412 741654
        if (test->tag == LE_LAST) {
413 13
                if (le->tag_last != tag)
414 0
                        ok = 0;
415 741654
        } else if (test->tag >= 0) {
416 741641
                if (test->tag != tag)
417 668365
                        ok = 0;
418 741641
        }
419 811409
        if (test->vre &&
420 639184
            test->tag >= 0 &&
421 639184
            test->tag == tag &&
422 69755
            VRE_ERROR_NOMATCH == VRE_match(test->vre, data, len, 0, NULL))
423 33089
                ok = 0;
424
425 741644
        alt = (test->skip_max == LE_ALT);
426 741644
        fail = (test->skip_max == LE_FAIL);
427
428 741644
        if (!ok && !alt && (test->skip_max == LE_ANY ||
429 128243
                    test->skip_max > le->skip_cnt))
430 577034
                skip = 1;
431
432 741644
        if (skip && vxid_ok && tag == SLT_End)
433 0
                fail = 1;
434
435 741644
        if (fail) {
436 124547
                if (ok) {
437 21
                        legend = "fail";
438 124547
                } else if (skip) {
439 0
                        legend = "end";
440 0
                        skip = 0;
441 124526
                } else if (le->m_arg) {
442 0
                        legend = "fmiss";
443 0
                } else {
444 124526
                        legend = NULL;
445
                }
446 124547
        }
447 617097
        else if (ok)
448 39963
                legend = "match";
449 577134
        else if (skip && le->m_arg)
450 483
                legend = "miss";
451 576651
        else if (skip || alt)
452 576651
                legend = NULL;
453
        else
454 0
                legend = "err";
455
456 741644
        if (legend != NULL)
457 80934
                vtc_log(le->vl, 4, "%-5s| %10ju %-15s %c %.*s",
458 40467
                    legend, (intmax_t)vxid, VSL_tags[tag], type, len,
459 40467
                    data);
460
461 741644
        if (ok) {
462 39985
                if (alt)
463 462
                        test->skip_max = LE_SEEN;
464 39985
                return (LEM_OK);
465
        }
466 701659
        if (alt) {
467 105
                test = logexp_alt(test);
468 105
                if (test == NULL)
469 0
                        return (LEM_FAIL);
470 105
                vtc_log(le->vl, 3, "alt  | %s", VSB_data(test->str));
471 105
                return (logexp_match(le, test, data, vxid, tag, type, len));
472
        }
473 701554
        if (skip)
474 577028
                return (LEM_SKIP);
475 124526
        return (LEM_FAIL);
476 741644
}
477
478
static enum le_match_e
479 617302
logexp_failchk(const struct logexp *le,
480
    const char *data, int64_t vxid, int tag, int type, int len)
481
{
482
        struct logexp_test *test;
483
        static enum le_match_e r;
484
485 617302
        if (VTAILQ_FIRST(&le->fail) == NULL)
486 535551
                return (LEM_SKIP);
487
488 206277
        VTAILQ_FOREACH(test, &le->fail, faillist) {
489 124547
                r = logexp_match(le, test, data, vxid, tag, type, len);
490 124547
                if (r == LEM_OK)
491 21
                        return (LEM_FAIL);
492 124526
                assert (r == LEM_FAIL);
493 124526
        }
494 81730
        return (LEM_OK);
495 617302
}
496
497
static int
498 992184
logexp_done(const struct logexp *le)
499
{
500 992184
        return ((VTAILQ_FIRST(&le->fail) == NULL) && le->test == NULL);
501
}
502
503
static int v_matchproto_(VSLQ_dispatch_f)
504 268897
logexp_dispatch(struct VSL_data *vsl, struct VSL_transaction * const pt[],
505
    void *priv)
506
{
507
        struct logexp *le;
508
        struct VSL_transaction *t;
509
        int i;
510
        enum le_match_e r;
511
        int64_t vxid;
512
        int tag, type, len;
513
        const char *data;
514
515 268897
        CAST_OBJ_NOTNULL(le, priv, LOGEXP_MAGIC);
516
517 531679
        for (i = 0; (t = pt[i]) != NULL; i++) {
518 886643
                while (1 == VSL_Next(t->c)) {
519 623861
                        if (!VSL_Match(vsl, t->c))
520 6531
                                continue;
521
522 617330
                        AN(t->c->rec.ptr);
523 617330
                        tag = VSL_TAG(t->c->rec.ptr);
524
525 617330
                        if (tag == SLT__Batch || tag == SLT_Witness)
526 68
                                continue;
527
528 617326
                        vxid = VSL_ID(t->c->rec.ptr);
529 617326
                        data = VSL_CDATA(t->c->rec.ptr);
530 617326
                        len = VSL_LEN(t->c->rec.ptr) - 1;
531 617326
                        type = VSL_CLIENT(t->c->rec.ptr) ? 'c' :
532 194657
                            VSL_BACKEND(t->c->rec.ptr) ? 'b' : '-';
533
534 617326
                        r = logexp_failchk(le, data, vxid, tag, type, len);
535 617326
                        if (r == LEM_FAIL)
536 21
                                return (r);
537 617305
                        if (le->test == NULL) {
538 315
                                assert (r == LEM_OK);
539 315
                                continue;
540
                        }
541
542 616990
                        CHECK_OBJ_NOTNULL(le->test, LOGEXP_TEST_MAGIC);
543
544 1233980
                        r = logexp_match(le, le->test,
545 616990
                            data, vxid, tag, type, len);
546 616990
                        if (r == LEM_FAIL)
547 0
                                return (r);
548 616990
                        if (r == LEM_SKIP) {
549 577027
                                le->skip_cnt++;
550 577027
                                continue;
551
                        }
552 39963
                        assert(r == LEM_OK);
553 39963
                        le->vxid_last = vxid;
554 39963
                        le->tag_last = tag;
555 39963
                        le->skip_cnt = 0;
556 39963
                        logexp_next(le);
557 39963
                        if (logexp_done(le))
558 7455
                                return (1);
559
                }
560 262782
        }
561 261417
        return (0);
562 268893
}
563
564
static void *
565 7258
logexp_thread(void *priv)
566
{
567
        struct logexp *le;
568
        int i;
569
570 7258
        CAST_OBJ_NOTNULL(le, priv, LOGEXP_MAGIC);
571 7258
        AN(le->run);
572 7258
        AN(le->vsm);
573 7258
        AN(le->vslq);
574
575 7258
        AZ(le->test);
576 7258
        vtc_log(le->vl, 4, "begin|");
577 7258
        if (le->query != NULL)
578 4263
                vtc_log(le->vl, 4, "qry  | %s", le->query);
579 7258
        logexp_next(le);
580 732621
        while (!logexp_done(le) && !vtc_stop && !vtc_error) {
581 725134
                i = VSLQ_Dispatch(le->vslq, logexp_dispatch, le);
582 725134
                if (i == 2 && le->err_arg) {
583 21
                        vtc_log(le->vl, 4, "done | failed as expected");
584 21
                        return (NULL);
585
                }
586 725113
                if (i == 2)
587 0
                        vtc_fatal(le->vl, "bad  | expectation failed");
588 725113
                else if (i < 0)
589 0
                        vtc_fatal(le->vl, "bad  | dispatch failed (%d)", i);
590 725113
                else if (i == 0 && ! logexp_done(le))
591 213131
                        VTIM_sleep(0.01);
592
        }
593 7497
        if (!logexp_done(le))
594 0
                vtc_fatal(le->vl, "bad  | outstanding expectations");
595 7497
        vtc_log(le->vl, 4, "done |");
596
597 7497
        return (NULL);
598 7518
}
599
600
static void
601 7518
logexp_close(struct logexp *le)
602
{
603
604 7518
        CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC);
605 7518
        AN(le->vsm);
606 7518
        if (le->vslq)
607 7518
                VSLQ_Delete(&le->vslq);
608 7518
        AZ(le->vslq);
609 7518
}
610
611
static void
612 7518
logexp_start(struct logexp *le)
613
{
614
        struct VSL_cursor *c;
615
616 7518
        CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC);
617 7518
        AN(le->vsl);
618 7518
        AZ(le->vslq);
619
620 7518
        AN(le->vsl);
621 7518
        (void)VSM_Status(le->vsm);
622 15036
        c = VSL_CursorVSM(le->vsl, le->vsm,
623 7518
            (le->d_arg ? 0 : VSL_COPT_TAIL) | VSL_COPT_BATCH);
624 7518
        if (c == NULL)
625 0
                vtc_fatal(le->vl, "VSL_CursorVSM: %s", VSL_Error(le->vsl));
626 7518
        le->vslq = VSLQ_New(le->vsl, &c, le->g_arg, le->query);
627 7518
        if (le->vslq == NULL) {
628 0
                VSL_DeleteCursor(c);
629 0
                vtc_fatal(le->vl, "VSLQ_New: %s", VSL_Error(le->vsl));
630
        }
631 7518
        AZ(c);
632
633 7518
        le->test = NULL;
634 7518
        le->skip_cnt = 0;
635 7518
        le->vxid_last = le->tag_last = -1;
636 7518
        le->run = 1;
637 7518
        PTOK(pthread_create(&le->tp, NULL, logexp_thread, le));
638 7518
}
639
640
static void
641 7518
logexp_wait(struct logexp *le)
642
{
643
        void *res;
644
645 7518
        CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC);
646 7518
        vtc_log(le->vl, 2, "Waiting for logexp");
647 7518
        PTOK(pthread_join(le->tp, &res));
648 7518
        logexp_close(le);
649 7518
        if (res != NULL && !vtc_stop)
650 0
                vtc_fatal(le->vl, "logexp returned \"%p\"", (char *)res);
651 7518
        le->run = 0;
652 7518
}
653
654
/* shared by expect and fail: parse from av[2] (vxid) onwards */
655
656
static void
657 42252
cmd_logexp_common(struct logexp *le, struct vtclog *vl,
658
    int skip_max, char * const *av)
659
{
660
        vre_t *vre;
661
        struct vsb vsb[1];
662
        int64_t vxid;
663
        int err, pos, tag;
664
        struct logexp_test *test;
665
        char *end, errbuf[VRE_ERROR_LEN];
666
667 42252
        if (!vstrcmp(av[2], "*"))
668 7833
                vxid = LE_ANY;
669 34419
        else if (!vstrcmp(av[2], "="))
670 22470
                vxid = LE_LAST;
671
        else {
672 11949
                vxid = strtoll(av[2], &end, 10);
673 11949
                if (*end != '\0' || vxid < 0)
674 0
                        vtc_fatal(vl, "Not a positive integer: '%s'", av[2]);
675
        }
676 42252
        if (!vstrcmp(av[3], "*"))
677 0
                tag = LE_ANY;
678 42252
        else if (!vstrcmp(av[3], "="))
679 0
                tag = LE_LAST;
680
        else {
681 42252
                tag = VSL_Name2Tag(av[3], vstrlen(av[3]));
682 42252
                if (tag < 0)
683 0
                        vtc_fatal(vl, "Unknown tag name: '%s'", av[3]);
684
        }
685 42252
        vre = NULL;
686 42252
        if (av[4]) {
687 36750
                vre = VRE_compile(av[4], 0, &err, &pos, 1);
688 36750
                if (vre == NULL) {
689 0
                        AN(VSB_init(vsb, errbuf, sizeof errbuf));
690 0
                        AZ(VRE_error(vsb, err));
691 0
                        AZ(VSB_finish(vsb));
692 0
                        VSB_fini(vsb);
693 0
                        vtc_fatal(vl, "Regex error (%s): '%s' pos %d",
694 0
                            errbuf, av[4], pos);
695
                }
696 36750
        }
697
698 42252
        ALLOC_OBJ(test, LOGEXP_TEST_MAGIC);
699 42252
        AN(test);
700 42252
        test->str = VSB_new_auto();
701 42252
        AN(test->str);
702 42252
        AZ(VSB_printf(test->str, "%s %s %s %s ", av[0], av[1], av[2], av[3]));
703 42252
        if (av[4])
704 36750
                VSB_quote(test->str, av[4], -1, 0);
705 42252
        AZ(VSB_finish(test->str));
706 42252
        test->skip_max = skip_max;
707 42252
        test->vxid = vxid;
708 42252
        test->tag = tag;
709 42252
        test->vre = vre;
710 42252
        VTAILQ_INSERT_TAIL(&le->tests, test, list);
711 42252
}
712
713
static void
714 39879
cmd_logexp_expect(CMD_ARGS)
715
{
716
        struct logexp *le;
717
        int skip_max;
718
        char *end;
719
720 39879
        CAST_OBJ_NOTNULL(le, priv, LOGEXP_MAGIC);
721 39879
        if (av[1] == NULL || av[2] == NULL || av[3] == NULL)
722 0
                vtc_fatal(vl, "Syntax error");
723
724 39879
        if (av[4] != NULL && av[5] != NULL)
725 0
                vtc_fatal(vl, "Syntax error");
726
727 39879
        if (!vstrcmp(av[1], "*"))
728 12873
                skip_max = LE_ANY;
729 27006
        else if (!vstrcmp(av[1], "?"))
730 462
                skip_max = LE_ALT;
731
        else {
732 26544
                skip_max = (int)strtol(av[1], &end, 10);
733 26544
                if (*end != '\0' || skip_max < 0)
734 0
                        vtc_fatal(vl, "Not a positive integer: '%s'", av[1]);
735
        }
736 39879
        cmd_logexp_common(le, vl, skip_max, av);
737 39879
}
738
739
static void
740 4452
cmd_logexp_fail(CMD_ARGS)
741
{
742
        struct logexp *le;
743
        struct logexp_test *test;
744
745 4452
        CAST_OBJ_NOTNULL(le, priv, LOGEXP_MAGIC);
746
747 4452
        if (av[1] == NULL)
748 0
                vtc_fatal(vl, "Syntax error");
749
750 4452
        if (!vstrcmp(av[1], "clear")) {
751 2079
                ALLOC_OBJ(test, LOGEXP_TEST_MAGIC);
752 2079
                AN(test);
753 2079
                test->skip_max = LE_CLEAR;
754 2079
                test->str = VSB_new_auto();
755 2079
                AN(test->str);
756 2079
                AZ(VSB_printf(test->str, "%s %s",
757
                    av[0], av[1]));
758 2079
                AZ(VSB_finish(test->str));
759
760 2079
                VTAILQ_INSERT_TAIL(&le->tests, test, list);
761 2079
                return;
762
        }
763
764 2373
        if (vstrcmp(av[1], "add"))
765 0
                vtc_fatal(vl, "Unknown fail argument '%s'", av[1]);
766
767 2373
        if (av[2] == NULL || av[3] == NULL)
768 0
                vtc_fatal(vl, "Syntax error");
769
770 2373
        cmd_logexp_common(le, vl, LE_FAIL, av);
771 4452
}
772
773
/* aid vsl debugging */
774
static void
775 0
cmd_logexp_abort(CMD_ARGS)
776
{
777
778
        struct logexp *le;
779
780 0
        CAST_OBJ_NOTNULL(le, priv, LOGEXP_MAGIC);
781
782 0
        cmd_logexp_common(le, vl, LE_ABORT, av);
783 0
}
784
785
static void
786 7434
logexp_spec(struct logexp *le, const char *spec)
787
{
788 7434
        CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC);
789
790 7434
        logexp_delete_tests(le);
791
792 7434
        parse_string(le->vl, le, spec);
793 7434
}
794
795
void
796 37296
cmd_logexpect(CMD_ARGS)
797
{
798
        struct logexp *le, *le2;
799
        int i;
800
801 37296
        (void)priv;
802
803 37296
        if (av == NULL) {
804
                /* Reset and free */
805 29568
                VTAILQ_FOREACH_SAFE(le, &logexps, list, le2) {
806 6237
                        CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC);
807 6237
                        VTAILQ_REMOVE(&logexps, le, list);
808 6237
                        if (le->run) {
809 0
                                (void)pthread_cancel(le->tp);
810 0
                                logexp_wait(le);
811 0
                        }
812 6237
                        logexp_delete(le);
813 6237
                }
814 23331
                return;
815
        }
816
817 13965
        AZ(vstrcmp(av[0], "logexpect"));
818 13965
        av++;
819
820 13965
        VTC_CHECK_NAME(vl, av[0], "Logexpect", 'l');
821 33936
        VTAILQ_FOREACH(le, &logexps, list) {
822 27699
                if (!vstrcmp(le->name, av[0]))
823 7728
                        break;
824 19971
        }
825 13965
        if (le == NULL) {
826 6237
                if (vstrcmp(av[1], "-v") || av[2] == NULL)
827 0
                        vtc_fatal(vl, "new logexp lacks -v");
828 6237
                le = logexp_new(av[0], av[2]);
829 6237
                av += 2;
830 6237
        }
831 13965
        av++;
832
833 47691
        for (; *av != NULL; av++) {
834 33726
                if (vtc_error)
835 0
                        break;
836 33726
                if (!vstrcmp(*av, "-wait")) {
837 6342
                        if (!le->run)
838 0
                                vtc_fatal(le->vl, "logexp not -started '%s'",
839 0
                                        *av);
840 6342
                        logexp_wait(le);
841 6342
                        continue;
842
                }
843
844
                /*
845
                 * We do an implicit -wait if people muck about with a
846
                 * running logexp.
847
                 */
848 27384
                if (le->run)
849 0
                        logexp_wait(le);
850 27384
                AZ(le->run);
851
852 27384
                if (!vstrcmp(*av, "-v")) {
853 420
                        if (av[1] == NULL || vstrcmp(av[1], le->vname))
854 0
                                vtc_fatal(le->vl, "-v argument cannot change");
855 420
                        av++;
856 420
                        continue;
857
                }
858 26964
                if (!vstrcmp(*av, "-d")) {
859 1617
                        if (av[1] == NULL)
860 0
                                vtc_fatal(le->vl, "Missing -d argument");
861 1617
                        le->d_arg = atoi(av[1]);
862 1617
                        av++;
863 1617
                        continue;
864
                }
865 25347
                if (!vstrcmp(*av, "-g")) {
866 5796
                        if (av[1] == NULL)
867 0
                                vtc_fatal(le->vl, "Missing -g argument");
868 5796
                        i = VSLQ_Name2Grouping(av[1], vstrlen(av[1]));
869 5796
                        if (i < 0)
870 0
                                vtc_fatal(le->vl, "Unknown grouping '%s'",
871 0
                                    av[1]);
872 5796
                        le->g_arg = (enum VSL_grouping_e)i;
873 5796
                        av++;
874 5796
                        continue;
875
                }
876 19551
                if (!vstrcmp(*av, "-q")) {
877 4032
                        if (av[1] == NULL)
878 0
                                vtc_fatal(le->vl, "Missing -q argument");
879 4032
                        REPLACE(le->query, av[1]);
880 4032
                        av++;
881 4032
                        continue;
882
                }
883 15519
                if (!vstrcmp(*av, "-m")) {
884 21
                        le->m_arg = !le->m_arg;
885 21
                        continue;
886
                }
887 15498
                if (!vstrcmp(*av, "-err")) {
888 21
                        le->err_arg = !le->err_arg;
889 21
                        continue;
890
                }
891 15477
                if (!vstrcmp(*av, "-start")) {
892 6342
                        logexp_start(le);
893 6342
                        continue;
894
                }
895 9135
                if (!vstrcmp(*av, "-run")) {
896 1176
                        logexp_start(le);
897 1176
                        logexp_wait(le);
898 1176
                        continue;
899
                }
900 7959
                if (**av == '-') {
901 525
                        if (av[1] != NULL) {
902 525
                                if (VSL_Arg(le->vsl, av[0][1], av[1])) {
903 525
                                        av++;
904 525
                                        continue;
905
                                }
906 0
                                vtc_fatal(le->vl, "%s", VSL_Error(le->vsl));
907
                        }
908 0
                        vtc_fatal(le->vl, "Unknown logexp argument: %s", *av);
909
                }
910 7434
                logexp_spec(le, *av);
911 7434
        }
912 37296
}
913
914
#endif /* VTEST_WITH_VTC_LOGEXPECT */