mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
217 lines
5.6 KiB
C
217 lines
5.6 KiB
C
|
|
/*
|
||
|
|
* testcode/lock_verify.c - verifier program for lock traces, checks order.
|
||
|
|
*
|
||
|
|
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||
|
|
*
|
||
|
|
* This software is open source.
|
||
|
|
*
|
||
|
|
* Redistribution and use in source and binary forms, with or without
|
||
|
|
* modification, are permitted provided that the following conditions
|
||
|
|
* are met:
|
||
|
|
*
|
||
|
|
* Redistributions of source code must retain the above copyright notice,
|
||
|
|
* this list of conditions and the following disclaimer.
|
||
|
|
*
|
||
|
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||
|
|
* this list of conditions and the following disclaimer in the documentation
|
||
|
|
* and/or other materials provided with the distribution.
|
||
|
|
*
|
||
|
|
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||
|
|
* be used to endorse or promote products derived from this software without
|
||
|
|
* specific prior written permission.
|
||
|
|
*
|
||
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
||
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* \file
|
||
|
|
*
|
||
|
|
* This file checks the lock traces generated by checklock.c.
|
||
|
|
* Checks if locks are consistently locked in the same order.
|
||
|
|
* If not, this can lead to deadlock if threads execute the different
|
||
|
|
* ordering at the same time.
|
||
|
|
*
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "config.h"
|
||
|
|
#include "util/log.h"
|
||
|
|
#include "util/rbtree.h"
|
||
|
|
|
||
|
|
/* --- data structures --- */
|
||
|
|
|
||
|
|
/** a lock */
|
||
|
|
struct order_lock {
|
||
|
|
/** rbnode in all tree */
|
||
|
|
rbnode_t node;
|
||
|
|
/** the thread id that created it */
|
||
|
|
int thr;
|
||
|
|
/** the instance number of creation */
|
||
|
|
int instance;
|
||
|
|
/** the creation file */
|
||
|
|
char* create_file;
|
||
|
|
/** creation line */
|
||
|
|
int create_line;
|
||
|
|
/** set of all locks that are smaller than this one (locked earlier) */
|
||
|
|
rbtree_t* smaller;
|
||
|
|
};
|
||
|
|
|
||
|
|
/** reference to a lock in a rbtree set */
|
||
|
|
struct lock_ref {
|
||
|
|
/** rbnode, key is an order_lock ptr */
|
||
|
|
rbnode_t node;
|
||
|
|
};
|
||
|
|
|
||
|
|
/** print program usage help */
|
||
|
|
static void
|
||
|
|
usage()
|
||
|
|
{
|
||
|
|
printf("lock_verify <trace files>\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/** compare two order_locks */
|
||
|
|
int order_lock_cmp(const void* e1, const void* e2)
|
||
|
|
{
|
||
|
|
struct order_lock* o1 = (struct order_lock*)e1;
|
||
|
|
struct order_lock* o2 = (struct order_lock*)e2;
|
||
|
|
if(o1->thr < o2->thr) return -1;
|
||
|
|
if(o1->thr > o2->thr) return 1;
|
||
|
|
if(o1->instance < o2->instance) return -1;
|
||
|
|
if(o1->instance > o2->instance) return 1;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** read header entry */
|
||
|
|
static void read_header(FILE* in)
|
||
|
|
{
|
||
|
|
time_t t;
|
||
|
|
pid_t p;
|
||
|
|
int thrno;
|
||
|
|
static int have_values = 0;
|
||
|
|
static time_t the_time;
|
||
|
|
static pid_t the_pid;
|
||
|
|
static int threads[256];
|
||
|
|
|
||
|
|
if(fread(&t, sizeof(t), 1, in) != 1 ||
|
||
|
|
fread(&thrno, sizeof(thrno), 1, in) != 1 ||
|
||
|
|
fread(&p, sizeof(p), 1, in) != 1) {
|
||
|
|
fatal_exit("fread: %s", strerror(errno));
|
||
|
|
}
|
||
|
|
/* check these values are sorta OK */
|
||
|
|
if(!have_values) {
|
||
|
|
the_time = t;
|
||
|
|
the_pid = p;
|
||
|
|
memset(threads, 0, 256*sizeof(int));
|
||
|
|
threads[thrno] = 1;
|
||
|
|
have_values = 1;
|
||
|
|
printf("Trace from pid %u on %s", (unsigned)p, ctime(&t));
|
||
|
|
} else {
|
||
|
|
if(the_pid != p)
|
||
|
|
fatal_exit("different pids in input files");
|
||
|
|
if(threads[thrno])
|
||
|
|
fatal_exit("same threadno in two files");
|
||
|
|
threads[thrno] = 1;
|
||
|
|
if( abs(the_time - t) > 3600)
|
||
|
|
fatal_exit("input files from different times: %u %u",
|
||
|
|
(unsigned)the_time, (unsigned)t);
|
||
|
|
}
|
||
|
|
printf("reading trace of thread %d\n", thrno);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** read a string from file, false on error */
|
||
|
|
static int readup_str(char** str, FILE* in)
|
||
|
|
{
|
||
|
|
#define STRMAX 1024
|
||
|
|
char buf[STRMAX];
|
||
|
|
int len = 0;
|
||
|
|
int c;
|
||
|
|
/* ends in zero */
|
||
|
|
while( (c = fgetc(in)) != 0) {
|
||
|
|
if(c == EOF)
|
||
|
|
fatal_exit("eof in readstr, file too short");
|
||
|
|
buf[len++] = c;
|
||
|
|
if(len == STRMAX) {
|
||
|
|
fatal_exit("string too long, bad file format");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
buf[len] = 0;
|
||
|
|
*str = strdup(buf);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** read creation entry */
|
||
|
|
static void read_create(rbtree_t* all, FILE* in)
|
||
|
|
{
|
||
|
|
struct order_lock* o = calloc(1, sizeof(struct order_lock));
|
||
|
|
if(!o) fatal_exit("malloc failure");
|
||
|
|
if(fread(&o->thr, sizeof(int), 1, in) != 1 ||
|
||
|
|
fread(&o->instance, sizeof(int), 1, in) != 1 ||
|
||
|
|
!readup_str(&o->create_file, in) ||
|
||
|
|
fread(&o->create_line, sizeof(int), 1, in) != 1)
|
||
|
|
fatal_exit("fread: %s", strerror(errno));
|
||
|
|
o->smaller = rbtree_create(order_lock_cmp);
|
||
|
|
o->node.key = o;
|
||
|
|
rbtree_insert(all, &o->node);
|
||
|
|
printf("read create %s %d\n", o->create_file, o->create_line);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** read lock entry */
|
||
|
|
static void read_lock(rbtree_t* all, FILE* in, int val)
|
||
|
|
{
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
/** read input file */
|
||
|
|
static void readinput(rbtree_t* all, char* file)
|
||
|
|
{
|
||
|
|
FILE *in = fopen(file, "r");
|
||
|
|
int fst;
|
||
|
|
if(!in) {
|
||
|
|
perror(file);
|
||
|
|
exit(1);
|
||
|
|
}
|
||
|
|
printf("reading file %s\n", file);
|
||
|
|
read_header(in);
|
||
|
|
while(fread(&fst, sizeof(fst), 1, in) == 1) {
|
||
|
|
if(fst == -1)
|
||
|
|
read_create(all, in);
|
||
|
|
else read_lock(all, in, fst);
|
||
|
|
}
|
||
|
|
fclose(in);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** main program to verify all traces passed */
|
||
|
|
int
|
||
|
|
main(int argc, char* argv[])
|
||
|
|
{
|
||
|
|
rbtree_t* all_locks;
|
||
|
|
int i;
|
||
|
|
if(argc <= 1) {
|
||
|
|
usage();
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
log_init(NULL);
|
||
|
|
log_ident_set("lock-verify");
|
||
|
|
/* init */
|
||
|
|
all_locks = rbtree_create(order_lock_cmp);
|
||
|
|
|
||
|
|
/* read the input files */
|
||
|
|
for(i=1; i<argc; i++) {
|
||
|
|
readinput(all_locks, argv[i]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* check ordering */
|
||
|
|
|
||
|
|
/* do not free a thing, OS will do it */
|
||
|
|
return 0;
|
||
|
|
}
|