📄 validator.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * *//**@IFILE validator.c * * SIP parser tester. * * @author Pekka Pessi <Pekka.Pessi@nokia.com>. * * @date Wed Mar 21 19:12:13 2001 ppessi */#include "config.h"#include <stdio.h>#include <string.h>#include <stddef.h>#include <stdlib.h>#include <limits.h>#include <assert.h>#include <sys/types.h>#include <sys/stat.h>#ifndef _WIN32#include <sys/mman.h>#endif#include <fcntl.h>#include <unistd.h>#include <errno.h>#include <sofia-sip/su_types.h>#include <sofia-sip/su_alloc_stat.h>#include <sofia-sip/su_time.h>#include <sofia-sip/su_tag.h>#include <sofia-sip/su_tag_class.h>#include <sofia-sip/su_tag_io.h>#include <sofia-sip/sip_tag.h>#include <sofia-sip/url_tag.h>#include <sofia-sip/sip.h>#include <sofia-sip/sip_header.h>#include <sofia-sip/msg_buffer.h>char const *name = "validator";typedef struct { unsigned o_verbose : 1; /**< Be verbose */ unsigned o_very_verbose : 1; /**< Be very verbose */ unsigned o_requests : 1; /**< Only requests */ unsigned o_responses : 1; /**< Only responses */ unsigned o_decode : 1; /**< Only try to decode, print error if unknown headers */ unsigned o_print : 1; /**< Print whole message */ unsigned o_times : 1; /**< Generate timing information */ unsigned o_memstats : 1; /**< Generate memory statistics */ unsigned o_histogram : 1; /**< Generate histograms */ unsigned o_sipstats : 1; /**< Generate header statistics */ unsigned o_vsipstats : 1; /**< Generate verbatim header statistics */ unsigned : 0; unsigned o_flags; /**< Message flags */} options_t;typedef struct { uint32_t N; uint32_t bsize; double buckets[32768];} histogram_t;statichistogram_t *histogram_create(uint64_t max, uint32_t bsize){ uint32_t N = (max + bsize - 1) / bsize; histogram_t *h = calloc(1, offsetof(histogram_t, buckets[N + 1])); if (!h) { perror("calloc"); exit(1); } h->N = N, h->bsize = bsize; return h;}staticdouble *histogram_update(histogram_t *h, uint32_t n){ if (h->bsize > 1) n /= h->bsize; if (n < h->N) return &h->buckets[n]; else return &h->buckets[h->N];}static voidhistogram_div(histogram_t *h, histogram_t const *n){ uint32_t i; assert(h->N == n->N); assert(h->bsize == n->bsize); for (i = 0; i <= h->N; i++) { if (n->buckets[i]) { h->buckets[i] /= n->buckets[i]; } else { assert(h->buckets[i] == 0); } }}typedef struct { uint64_t number; uint64_t headers; uint64_t payloads; uint64_t pl_bytes;} sipstat_t;typedef struct { sipstat_t req, resp; histogram_t *hist_headers;} sipstats_t;typedef struct { char const *name; char const *sep; uint64_t messages; uint64_t bytes; uint64_t errors; uint32_t files; double time; options_t options[1]; /* Statistics */ histogram_t *hist_msgsize; histogram_t *hist_mallocs; histogram_t *hist_memsize; histogram_t *hist_nheaders; sipstats_t sipstats[1]; su_home_stat_t hs[1]; uint64_t est_fail, est_succ, est_slack;} context_t;void usage(void){ fprintf(stderr, "usage: %s [-vdp]\n", name); exit(2);}char *lastpart(char *path){ char *p = strrchr(path, '/'); if (p) return p + 1; else return path;}msg_mclass_t *mclass = NULL;int validate_file(int fd, char const *name, context_t *ctx);int validate_dump(char *, off_t, context_t *ctx);int report(context_t const *ctx);static void memstats(msg_t *, uint32_t msize, context_t *ctx);static void sipstats(msg_t *, uint32_t msize, sipstats_t *ss, context_t *ctx);int main(int argc, char *argv[]){ context_t ctx[1] = {{ 0 }}; options_t *o = ctx->options; name = lastpart(argv[0]); /* Set our name */ for (; argv[1]; argv++) { if (argv[1][0] == 0) usage(); else if (argv[1][0] != '-') break; else if (argv[1][1] == 0) { argv++; break; } else if (strcmp(argv[1], "-v") == 0) o->o_very_verbose = o->o_verbose, o->o_verbose = 1; else if (strcmp(argv[1], "-d") == 0) o->o_decode = 1; /* Decode only */ else if (strcmp(argv[1], "-p") == 0) o->o_print = 1; else if (strcmp(argv[1], "-q") == 0) o->o_requests = 1; else if (strcmp(argv[1], "-Q") == 0) o->o_responses = 1; else if (strcmp(argv[1], "-t") == 0) o->o_times = 1; else if (strcmp(argv[1], "-m") == 0) o->o_memstats = 1; else if (strcmp(argv[1], "-s") == 0) o->o_vsipstats = o->o_sipstats, o->o_sipstats = 1; else if (strcmp(argv[1], "-h") == 0) o->o_histogram = 1; else usage(); } if (o->o_requests && o->o_responses) usage(); if (!mclass) mclass = sip_default_mclass(); if (argv[1]) { for (; argv[1]; argv++) { int fd = open(argv[1], O_RDONLY, 000); if (fd == -1) perror(argv[1]), exit(1); if (validate_file(fd, argv[1], ctx)) exit(1); close(fd); } } else validate_file(0, "", ctx); report(ctx); exit(0);}int validate_file(int fd, char const *name, context_t *ctx){ void *p; off_t size; int retval; ctx->name = name; if (strlen(name)) ctx->sep = ": "; else ctx->sep = ""; ctx->files++; size = lseek(fd, 0, SEEK_END); if (size < 1) return 0; if (size > INT_MAX) { fprintf(stderr, "%s%stoo large file to map\n", ctx->name, ctx->sep); return -1; }#ifndef _WIN32 p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0L); if (p == NULL) { perror("mmap"); return -1; } retval = validate_dump(p, size, ctx); munmap(p, size); return retval;#else errno = EINVAL; perror("mmap not implemented"); return -1;#endif }static inline void nul_terminate(char *b, off_t size){ char *end; /* NUL-terminate */ for (end = b + size - 1; end != b; end--) if (*end == '\v') break; *end = '\0';}static inlineint search_msg(char **bb, char const *protocol){ int linelen, plen = strlen(protocol); char *b = *bb; for (;;) { if (!b[0]) return 0; if (strncmp(b, protocol, plen) == 0 && b[plen] == ' ') return 1; /* status */ linelen = strcspn(b, "\r\n"); if (linelen > plen + 1 && b[linelen - plen - 1] == ' ' && strncmp(b + linelen - plen, protocol, plen) == 0) return 1; /* request */ b += linelen + strspn(b + linelen, "\r\n"); *bb = b; }}int validate_dump(char *b, off_t size, context_t *ctx){ int n = 0, N = 0; struct message { char *b; int size; } *msgs = NULL; uint64_t time0, time1; options_t *o = ctx->options; int maxsize = 0; nul_terminate(b, size); /* Split dump file to messages */ while (search_msg(&b, SIP_VERSION_CURRENT)) { int msize = strcspn(b, "\v"); int linelen = strcspn(b, "\r\n"); if (o->o_responses && memcmp(b, SIP_VERSION_CURRENT, strlen(SIP_VERSION_CURRENT)) != 0) ; else if (o->o_requests && memcmp(b, SIP_VERSION_CURRENT, strlen(SIP_VERSION_CURRENT)) == 0) ; else { if (o->o_very_verbose) printf("message %d: %.*s\n", n, linelen, b); if (n == N) { N *= 2; if (n == N) N = 16; msgs = realloc(msgs, sizeof(*msgs) * N); if (msgs == NULL) { perror("realloc"); exit(1); } } msgs[n].b = b; msgs[n].size = msize; n++; ctx->bytes += msize; if (msize > maxsize) maxsize = msize; } b += msize; if (*b) *b++ = '\0'; } ctx->messages += N = n; if (o->o_histogram) { ctx->hist_msgsize = histogram_create(maxsize, 64); if (o->o_memstats) { ctx->hist_mallocs = histogram_create(maxsize, 64); ctx->hist_memsize = histogram_create(maxsize, 64); } if (o->o_sipstats) { ctx->sipstats->hist_headers = histogram_create(64, 1); ctx->hist_nheaders = histogram_create(maxsize, 64); } } time0 = su_nanocounter(); for (n = 0; n < N; n++) { msg_t *msg = msg_create(mclass, o->o_flags); int m; if (msg == NULL) { perror("msg_create"); exit(1); } if (o->o_memstats) su_home_init_stats(msg_home(msg)); msg_buf_set(msg, msgs[n].b, msgs[n].size + 1); msg_buf_commit(msg, msgs[n].size, 1); su_home_preload(msg_home(msg), 1, msgs[n].size + 384); m = msg_extract(msg); if (m < 0) { fprintf(stderr, "%s%sparsing error in message %d\n", ctx->name, ctx->sep, n); ctx->errors++; } else { if (ctx->hist_msgsize) *histogram_update(ctx->hist_msgsize, msgs[n].size) += 1; if (o->o_sipstats) sipstats(msg, msgs[n].size, ctx->sipstats, ctx); if (o->o_memstats) memstats(msg, msgs[n].size, ctx); } msg_destroy(msg); } time1 = su_nanocounter(); if (o->o_times) { double dur = (time1 - time0) * 1E-9; ctx->time += dur; printf("%s%s%d messages in %g seconds (%g msg/sec)\n" " parse speed %.1f Mb/s (on Ethernet wire %.1f Mb/s)\n", ctx->name, ctx->sep, N, dur, (double)N / dur, (double)ctx->bytes * 8 / ctx->time / 1e6, ((double)ctx->bytes + N * (16 + 20 + 8)) * 8 / ctx->time / 1e6); } free(msgs); return 0;}typedef unsigned longlong ull;staticvoid report_memstats(char const *title, su_home_stat_t const hs[1]){ printf("%s%smemory statistics\n", title, strlen(title) ? " " : ""); if (hs->hs_allocs.hsa_number) printf("\t"LLU" allocs, "LLU" bytes, "LLU" rounded," " "LLU" max\n", (ull)hs->hs_allocs.hsa_number, (ull)hs->hs_allocs.hsa_bytes, (ull)hs->hs_allocs.hsa_rbytes, (ull)hs->hs_allocs.hsa_maxrbytes); if (hs->hs_frees.hsf_number) printf("\t"LLU" frees, "LLU" bytes, rounded to "LLU" bytes\n", (ull)hs->hs_frees.hsf_number, (ull)hs->hs_frees.hsf_bytes, (ull)hs->hs_frees.hsf_rbytes); if (hs->hs_rehash || hs->hs_clones) printf("\t%d rehashes, %d clones\n", hs->hs_rehash, hs->hs_clones);}void memstats(msg_t *msg, uint32_t msize, context_t *ctx){ options_t *o = ctx->options; su_home_stat_t hs[1]; su_home_get_stats(msg_home(msg), 1, hs, sizeof(hs)); su_home_stat_add(ctx->hs, hs); if (o->o_histogram) { *histogram_update(ctx->hist_mallocs, msize) += hs->hs_allocs.hsa_number; *histogram_update(ctx->hist_memsize, msize) += hs->hs_allocs.hsa_maxrbytes; } { int estimate = msize + 384; int slack = estimate - hs->hs_allocs.hsa_maxrbytes; if (slack < 0) ctx->est_fail++; else { ctx->est_succ++; ctx->est_slack += slack; } } if (o->o_very_verbose) report_memstats(ctx->name, hs);}void report_sipstat(char const *what, sipstat_t const *sss){ printf("%s: "LLU" with %.1f headers (total "LLU")\n", what, (ull)sss->number, (double)sss->headers / sss->number, (ull)sss->headers); if (sss->payloads) printf("\t"LLU" with body of %.1f bytes (total "LLU")\n", (ull)sss->payloads, (double)sss->pl_bytes / sss->payloads, (ull)sss->payloads);}void sipstats(msg_t *msg, uint32_t msize, sipstats_t *ss, context_t *ctx){ options_t *o = ctx->options; msg_pub_t *m = msg_object(msg); sip_t const *sip = sip_object(msg); msg_header_t *h; sipstat_t *sss; uint32_t n, bytes; if (!sip) return; if (m->msg_request) { sss = &ss->req; h = m->msg_request; } else if (m->msg_status) { sss = &ss->resp; h = m->msg_status; } else { return; } sss->number++; /* Count headers */ for (n = 0, h = h->sh_succ; h && !sip_is_separator((sip_header_t *)h); h = h->sh_succ) n++; sss->headers += n; bytes = sip->sip_payload ? sip->sip_payload->pl_len : 0; if (bytes) { sss->payloads++; sss->pl_bytes += bytes; } if (ctx->hist_nheaders) { *histogram_update(ctx->hist_nheaders, msize) += n; *histogram_update(ss->hist_headers, n) += 1; } if (o->o_very_verbose) printf("%s%s%d headers, %d bytes in payload\n", ctx->name, ctx->sep, n, bytes);}void report_histogram(char const *title, histogram_t const *h){ int i, min_i, max_i; for (i = 0; i < h->N && h->buckets[i] == 0.0; i++) ; min_i = i; for (i = h->N - 1; i >= 0 && h->buckets[i] == 0.0; i--) ; max_i = i; if (min_i >= max_i) return; printf("%s histogram\n", title); for (i = min_i; i < max_i; i++) printf("\t%d..%d: %.1f\n", i * h->bsize, (i + 1) * h->bsize, h->buckets[i]); if (h->buckets[h->N]) printf("\t%d..: %.1f\n", h->N * h->bsize, h->buckets[h->N]);}int report(context_t const *ctx){ options_t *o = ctx->options; uint64_t n = ctx->messages; if (!n) return -1; printf("total "LLU" messages with "LLU" bytes (mean size "LLU")\n", (ull)n, (ull)ctx->bytes, (ull)(ctx->bytes / n)); if (ctx->hist_msgsize) report_histogram("Message size", ctx->hist_msgsize); if (o->o_times && ctx->files > 1) printf("total "LLU" messages in %g seconds (%g msg/sec)\n", (ull)n, ctx->time, (double)n / ctx->time); if (o->o_sipstats) { sipstats_t *ss = ctx->sipstats; report_sipstat("requests", &ss->req); report_sipstat("responses", &ss->resp); if (ctx->hist_nheaders) { histogram_div(ctx->hist_nheaders, ctx->hist_msgsize); report_histogram("Number of headers", ctx->hist_nheaders); } } if (o->o_memstats) { su_home_stat_t hs[1]; *hs = *ctx->hs; report_memstats("total", hs); /* Calculate mean */ hs->hs_clones /= n; hs->hs_rehash /= n; hs->hs_allocs.hsa_number /= n; hs->hs_allocs.hsa_bytes /= n; hs->hs_allocs.hsa_rbytes /= n; hs->hs_allocs.hsa_maxrbytes /= n; hs->hs_frees.hsf_number /= n; hs->hs_frees.hsf_bytes /= n; hs->hs_frees.hsf_rbytes /= n; hs->hs_blocks.hsb_number /= n; hs->hs_blocks.hsb_bytes /= n; hs->hs_blocks.hsb_rbytes /= n; report_memstats("mean", hs); printf("\testimator fails %.1f%% times (mean slack %.0f bytes)\n", 100 * (double)ctx->est_fail / (ctx->est_fail + ctx->est_succ), (double)ctx->est_slack / ctx->est_succ); if (ctx->hist_memsize) { histogram_div(ctx->hist_memsize, ctx->hist_msgsize); report_histogram("Allocated memory", ctx->hist_memsize); } } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -