terry%mozilla.org fa47388c7d I finally came to my senses and made a separate src subdirectory (so that I can add other directories for tests and stuff.)
git-svn-id: svn://10.0.0.236/trunk@38705 18797224-902f-48f8-a5cc-f745e15eee43
1999-07-07 18:03:15 +00:00

301 lines
8.3 KiB
C

/* -*- Mode: C; indent-tabs-mode: nil; -*-
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the TripleDB database library.
*
* The Initial Developer of the Original Code is Geocast Network Systems.
* Portions created by Geocast are Copyright (C) 1999 Geocast Network Systems.
* All Rights Reserved.
*
* Contributor(s): Terry Weissman <terry@mozilla.org>
*/
/* Various debugging routines. */
#include "tdbtypes.h"
#include "tdbdebug.h"
#include "prprf.h"
void TDBDumpNode(PRFileDesc* fid, TDBNode* node)
{
switch(node->type) {
case TDBTYPE_STRING:
PR_fprintf(fid, "%s", node->d.str.string);
break;
default:
PR_fprintf(fid, "%ld", node->d.i);
}
}
void TDBDumpRecord(PRFileDesc* fid, TDB* db, TDBPtr ptr, PRInt32 tree, int indent, const char* kidname)
{
TDBRecord* record;
int i;
TDBPtr kid0;
TDBPtr kid1;
if (ptr == 0) return;
record = tdbLoadRecord(db, ptr);
for (i=0 ; i<indent ; i++) {
PR_fprintf(fid, " ");
}
PR_fprintf(fid, "[%s] pos=%d, len=%d, bal=%d ", kidname, record->position,
record->length, record->entry[tree].balance);
TDBDumpNode(fid, record->data[0]);
PR_fprintf(fid, ",");
TDBDumpNode(fid, record->data[1]);
PR_fprintf(fid, ",");
TDBDumpNode(fid, record->data[2]);
PR_fprintf(fid, "\n");
kid0 = record->entry[tree].child[0];
kid1 = record->entry[tree].child[1];
tdbFlush(db);
TDBDumpRecord(fid, db, kid0, tree, indent + 1, "0");
TDBDumpRecord(fid, db, kid1, tree, indent + 1, "1");
}
void TDBDumpTree(PRFileDesc* fid, TDB* db, PRInt32 tree)
{
TDBPtr root;
if (tree < 0) {
root = db->freeroot;
tree = 0;
} else {
root = db->roots[tree];
}
TDBDumpRecord(fid, db, root, tree, 0, "root");
}
typedef struct _TDBRangeSet {
PRInt32 position;
PRInt32 length;
struct _TDBRangeSet* next;
struct _TDBRangeSet* prev;
} TDBRangeSet;
static PRStatus checkMerges(TDBRangeSet* set, TDBRangeSet* tmp)
{
while (tmp->next != set &&
tmp->position + tmp->length == tmp->next->position) {
TDBRangeSet* t = tmp->next;
tmp->next = t->next;
tmp->next->prev = tmp;
tmp->length += t->length;
PR_Free(t);
}
while (tmp->prev != set &&
tmp->position == tmp->prev->position + tmp->prev->length) {
TDBRangeSet* t = tmp->prev;
tmp->prev = t->prev;
tmp->prev->next = tmp;
tmp->position = t->position;
t->length += t->length;
PR_Free(t);
}
return PR_SUCCESS;
}
static PRStatus addRange(TDBRangeSet* set, PRInt32 position, PRInt32 length)
{
TDBRangeSet* tmp;
TDBRangeSet* this;
for (tmp = set->next ; tmp != set ; tmp = tmp->next) {
if ((position >= tmp->position &&
position < tmp->position + tmp->length) ||
(tmp->position >= position &&
tmp->position < position + length)) {
PR_ASSERT(0); /* Collision! */
return PR_FAILURE;
}
if (position + length == tmp->position) {
tmp->position = position;
tmp->length += length;
return checkMerges(set, tmp);
} else if (tmp->position + tmp->length == position) {
tmp->length += length;
return checkMerges(set, tmp);
}
if (tmp->position > position + length) {
break;
}
}
this = PR_NEWZAP(TDBRangeSet);
if (!this) return PR_FAILURE;
this->next = tmp;
this->prev = tmp->prev;
this->next->prev = this;
this->prev->next = this;
this->position = position;
this->length = length;
return PR_SUCCESS;
}
typedef struct _TDBTreeInfo {
TDB* db;
PRInt32 tree; /* Which tree we're analyzing */
PRInt32 comparerule;
PRInt32 count; /* Number of nodes in tree. */
TDBRangeSet set; /* Range of bytes used by tree. */
PRInt32 maxdepth; /* Maximum depth of tree. */
} TDBTreeInfo;
static PRStatus checkTree(TDBTreeInfo* info, TDBPtr ptr, int depth,
int* maxdepth, TDBRecord* parent, int kid)
{
TDBRecord* record;
PRStatus status;
TDBPtr kid0;
TDBPtr kid1;
int d0 = depth;
int d1 = depth;
PRInt8 bal;
TDBRecord keep;
PRInt64 cmp;
int i;
if (ptr == 0) return PR_SUCCESS;
info->count++;
if (*maxdepth < depth) {
*maxdepth = depth;
}
record = tdbLoadRecord(info->db, ptr);
if (parent) {
cmp = tdbCompareRecords(record, parent, info->comparerule);
PR_ASSERT((kid == 0 && cmp < 0) || (kid == 1 && cmp > 0));
if (! ((kid == 0 && cmp < 0) || (kid == 1 && cmp > 0))) {
return PR_FAILURE;
}
}
status = addRange(&(info->set), record->position, record->length);
if (status != PR_SUCCESS) return status;
bal = record->entry[info->tree].balance;
if (bal < -1 || bal > 1) {
PR_ASSERT(0);
return PR_FAILURE;
}
kid0 = record->entry[info->tree].child[0];
kid1 = record->entry[info->tree].child[1];
memcpy(&keep, record, sizeof(TDBRecord));
for (i=0 ; i<3 ; i++) {
keep.data[i] = tdbNodeDup(keep.data[i]);
}
tdbFlush(info->db);
status = checkTree(info, kid0, depth + 1, &d0, &keep, 0);
if (status != PR_SUCCESS) return status;
status = checkTree(info, kid1, depth + 1, &d1, &keep, 1);
if (status != PR_SUCCESS) return status;
for (i=0 ; i<3 ; i++) {
TDBFreeNode(keep.data[i]);
}
if (d1 - d0 != bal) {
PR_ASSERT(0);
return PR_FAILURE;
}
if (*maxdepth < d0) {
*maxdepth = d0;
}
if (*maxdepth < d1) {
*maxdepth = d1;
}
return PR_SUCCESS;
}
PRStatus TDBSanityCheck(TDB* db, PRFileDesc* fid)
{
TDBTreeInfo info;
TDBRangeSet* tmp;
int i;
for (i=-1 ; i<4 ; i++) {
info.set.next = &(info.set);
info.set.prev = &(info.set);
info.db = db;
info.tree = (i < 0 ? 0 : i);
info.comparerule = i;
info.maxdepth = 0;
info.count = 0;
PR_fprintf(fid, "Checking tree %d...\n", i);
if (checkTree(&info, i < 0 ? db->freeroot : db->roots[i], 0,
&(info.maxdepth), NULL, 0) != PR_SUCCESS) {
PR_fprintf(fid, "Problem found!\n");
return PR_FAILURE;
}
for (tmp = info.set.next; tmp != &(info.set) ; tmp = tmp->next) {
PR_fprintf(fid, " %d - %d (%d)\n",
tmp->position, tmp->position + tmp->length,
tmp->length);
}
PR_fprintf(fid, " maxdepth: %d count %d\n", info.maxdepth,
info.count);
}
return PR_SUCCESS;
}
extern void TDBGetCursorStats(TDBCursor* cursor,
PRInt32* hits,
PRInt32* misses)
{
*hits = cursor->hits;
*misses = cursor->misses;
}
void makeDotEntry(TDB* db, PRFileDesc* fid, TDBPtr ptr, PRInt32 tree)
{
TDBRecord* record;
TDBPtr kid[2];
int i;
record = tdbLoadRecord(db, ptr);
PR_fprintf(fid, "%d [label=\"%d\\n", record->position, record->position);
for (i=0 ; i<3 ; i++) {
TDBDumpNode(fid, record->data[i]);
if (i<2) PR_fprintf(fid, ",");
}
PR_fprintf(fid, "\\nbal=%d\"]\n", record->entry[tree].balance);
kid[0] = record->entry[tree].child[0];
kid[1] = record->entry[tree].child[1];
/* tdbFlush(db); */
for (i=0 ; i<2 ; i++) {
if (kid[i] != 0) {
makeDotEntry(db, fid, kid[i], tree);
PR_fprintf(fid, "%d -> %d [label=\"%d\"]\n", ptr, kid[i], i);
}
}
}
void TDBMakeDotGraph(TDB* db, const char* filename, PRInt32 tree)
{
TDBPtr root;
PRFileDesc* fid;
fid = PR_Open(filename, PR_WRONLY | PR_CREATE_FILE, 0666);
if (tree == -1) {
root = db->freeroot;
tree = 0;
} else {
root = db->roots[tree];
}
PR_fprintf(fid, "digraph G {\n");
makeDotEntry(db, fid, root, tree);
PR_fprintf(fid, "}\n");
PR_Close(fid);
}