blob: 9d4bc7e0bd07816d53636098e1d2ab4f872c6028 [file] [log] [blame]
/*
* debugutils.c
*
* Routines for debugging Greenplum DB
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
#include "postgres.h"
#include "fmgr.h"
#include <stdio.h>
#include <unistd.h>
#include "nodes/plannodes.h"
#include "utils/debugutils.h"
#include "utils/lsyscache.h"
static void
printatt(unsigned attributeId,
Form_pg_attribute attributeP,
char *value, char *buf)
{
sprintf(buf,
"\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)|",
attributeId,
NameStr(attributeP->attname),
value != NULL ? " = \"" : "",
value != NULL ? value : "",
value != NULL ? "\"" : "",
(unsigned int) (attributeP->atttypid),
attributeP->attlen,
attributeP->atttypmod,
attributeP->attbyval ? 't' : 'f');
}
/*
* Return a human readable string representation of a tuple. The returned
* string must be pfree()d by the caller.
*/
char *
tup2str(TupleTableSlot *slot)
{
TupleDesc typeinfo = slot->tts_tupleDescriptor;
int natts = typeinfo->natts;
int i;
Datum origattr,
attr;
char *value;
bool isnull;
Oid typoutput;
bool typisvarlena;
char *buf;
buf = palloc(10 * 1024);
buf[0] = '\0';
for (i = 0; i < natts; ++i)
{
origattr = slot_getattr(slot, i + 1, &isnull);
if (isnull)
continue;
getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
&typoutput, &typisvarlena);
/*
* If we have a toasted datum, forcibly detoast it here to avoid
* memory leakage inside the type's output routine.
*/
if (typisvarlena)
attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
else
attr = origattr;
value = OidOutputFunctionCall(typoutput, attr);
printatt((unsigned) i + 1, typeinfo->attrs[i], value,
&buf[strlen(buf)]);
pfree(value);
/* Clean up detoasted copy, if any */
if (attr != origattr)
pfree(DatumGetPointer(attr));
}
return buf;
}
extern char *plannode_type(Plan *p);
static void
drawnode(FILE *ofile, Plan *plan, List *nodelist)
{
if(!plan)
return;
/* print out the node */
fprintf(ofile, "\"Node_0x%p\" [\n", plan);
fprintf(ofile, "label=\"%s %p flow %p\"\n", plannode_type(plan), plan, plan->flow);
fprintf(ofile, "];\n");
if(IsA(plan, Append))
{
Append *app = (Append *) plan;
ListCell *cell;
foreach(cell, app->appendplans)
{
Plan *child = (Plan *) lfirst(cell);
if(!list_member_ptr(nodelist, child))
{
nodelist = lappend(nodelist, child);
drawnode(ofile, child, nodelist);
}
fprintf(ofile, "\"Node_0x%p\" -> \"Node_0x%p\" [\n", plan, child);
fprintf(ofile, "];\n");
}
}
else if (IsA(plan, SubqueryScan))
{
SubqueryScan *subq = (SubqueryScan *) plan;
Plan *child = subq->subplan;
if(!list_member_ptr(nodelist, child))
{
nodelist = lappend(nodelist, child);
drawnode(ofile, child, nodelist);
}
fprintf(ofile, "\"Node_0x%p\" -> \"Node_0x%p\" [\n", plan, child);
fprintf(ofile, "];\n");
}
else
{
Plan *child = plan->lefttree;
if(child)
{
if(!list_member_ptr(nodelist, child))
{
nodelist = lappend(nodelist, child);
drawnode(ofile, child, nodelist);
}
fprintf(ofile, "\"Node_0x%p\" -> \"Node_0x%p\" [\n", plan, child);
fprintf(ofile, "];\n");
}
child = plan->righttree;
if(child)
{
if(!list_member_ptr(nodelist, child))
{
nodelist = lappend(nodelist, child);
drawnode(ofile, child, nodelist);
}
fprintf(ofile, "\"Node_0x%p\" -> \"Node_0x%p\" [\n", plan, child);
fprintf(ofile, "];\n");
}
}
}
/*
* Write out a plan to be interpretted by dot(1). Tools available at
* http://www.graphviz.org
*/
void
dotnode(void *node, const char *fname)
{
List *nodelist = NULL;
FILE *ofile = fopen(fname, "w+");
/* Print dot header */
fprintf(ofile, "digraph g {\n");
fprintf(ofile, "graph [\n");
fprintf(ofile, "];\n");
fprintf(ofile, "node [\n");
fprintf(ofile, "fontsize = \"14\"\n");
fprintf(ofile, "shape = \"box\"\n");
fprintf(ofile, "];\n");
fprintf(ofile, "edge [\n");
fprintf(ofile, "fontsize = \"14\"\n");
fprintf(ofile, "];\n");
if(node)
{
nodelist = lappend(nodelist, node);
drawnode(ofile, (Plan *) node, nodelist);
}
fprintf(ofile, "}\n");
fclose(ofile);
}
/*
* dump a tupledesc
*/
void dump_tupdesc(TupleDesc tupdesc, const char *fname)
{
FILE *ofile = fopen(fname, "w+");
int i;
fprintf(ofile, "TupleDesc: natts %d, hasoid %s\n", tupdesc->natts, tupdesc->tdhasoid ? "true" : "false");
fprintf(ofile, "Name\t\tattlen\tattbyval\tattalign\n");
fprintf(ofile, "==================================\n");
for (i=0; i<tupdesc->natts; ++i)
{
Form_pg_attribute attr = tupdesc->attrs[i];
fprintf(ofile, "%s, %d, %s, %c\n",
attr->attname.data, attr->attlen,
attr->attbyval ? "true" : "false",
attr->attalign
);
}
fclose(ofile);
}
/*
* dump a memtuple binding
*/
void dump_mt_bind(MemTupleBinding *mt_bind, const char *fname)
{
FILE *ofile = fopen(fname, "w+");
int i;
fprintf(ofile, "Mt_bind: column_align %d, nbm_extra_size %d\n",
mt_bind->column_align, mt_bind->null_bitmap_extra_size);
fprintf(ofile, "TupleDesc: natts %d, hasoid %s\n",
mt_bind->tupdesc->natts,
mt_bind->tupdesc->tdhasoid ? "true" : "false");
fprintf(ofile, " Small binding: vastart %d\n", mt_bind->bind.var_start);
fprintf(ofile, "Name\t\tattlen\tattbyval\tattalign\toffset\tlen\tflag\tnb\tnm\tns\n");
fprintf(ofile, "==================================\n");
for (i=0; i<mt_bind->tupdesc->natts; ++i)
{
Form_pg_attribute attr = mt_bind->tupdesc->attrs[i];
fprintf(ofile, "%s, %d, %s, %c, %d, %d, %d, %d, %d, %d\n",
attr->attname.data, attr->attlen,
attr->attbyval ? "true" : "false",
attr->attalign,
mt_bind->bind.bindings[i].offset,
mt_bind->bind.bindings[i].len,
mt_bind->bind.bindings[i].flag,
mt_bind->bind.bindings[i].null_byte,
mt_bind->bind.bindings[i].null_mask,
mt_bind->bind.null_saves[i]
);
}
fprintf(ofile, "\n\n Large binding: vastart %d\n", mt_bind->large_bind.var_start);
fprintf(ofile, "Name\t\tattlen\tattbyval\tattalign\toffset\tlen\tflag\tnb\tnm\tns\n");
fprintf(ofile, "==================================\n");
for (i=0; i<mt_bind->tupdesc->natts; ++i)
{
Form_pg_attribute attr = mt_bind->tupdesc->attrs[i];
fprintf(ofile, "%s, %d, %s, %c, %d, %d, %d, %d, %d, %d\n",
attr->attname.data, attr->attlen,
attr->attbyval ? "true" : "false",
attr->attalign,
mt_bind->large_bind.bindings[i].offset,
mt_bind->large_bind.bindings[i].len,
mt_bind->large_bind.bindings[i].flag,
mt_bind->large_bind.bindings[i].null_byte,
mt_bind->large_bind.bindings[i].null_mask,
mt_bind->large_bind.null_saves[i]
);
}
}
#ifdef USE_ASSERT_CHECKING
/*
* Debugging - create/overwrite named file with contents of string.
*
* Use, e.g., with gdb to save debugging output.
*/
#include <stdio.h>
#include <string.h>
int debug_write(const char *filename, const char *output_string)
{
FILE *file;
file = fopen(filename, "w");
if ( !file )
{
fprintf(stderr, "debug_write: can't open \"%s\" for output", filename);
return 1;
}
if ( fprintf(file, "%s\n", output_string) == EOF )
{
fprintf(stderr, "debug_write: can't write to \"%s\"", filename);
fclose(file);
return 1;
}
fclose(file);
return 0;
}
#endif