root/usr/src/tools/smatch/src/graph.c
/* Copyright © International Business Machines Corp., 2006
 *              Adelard LLP, 2007
 *
 * Author: Josh Triplett <josh@freedesktop.org>
 *         Dan Sheridan <djs@adelard.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>

#include "lib.h"
#include "allocate.h"
#include "token.h"
#include "parse.h"
#include "symbol.h"
#include "expression.h"
#include "linearize.h"


/* Draw the subgraph for a given entrypoint. Includes details of loads
 * and stores for globals, and marks return bbs */
static void graph_ep(struct entrypoint *ep)
{
        struct basic_block *bb;
        struct instruction *insn;

        const char *fname, *sname;

        fname = show_ident(ep->name->ident);
        sname = stream_name(ep->entry->bb->pos.stream);

        printf("subgraph cluster%p {\n"
               "    color=blue;\n"
               "    label=<<TABLE BORDER=\"0\" CELLBORDER=\"0\">\n"
               "             <TR><TD>%s</TD></TR>\n"
               "             <TR><TD><FONT POINT-SIZE=\"21\">%s()</FONT></TD></TR>\n"
               "           </TABLE>>;\n"
               "    file=\"%s\";\n"
               "    fun=\"%s\";\n"
               "    ep=bb%p;\n",
               ep, sname, fname, sname, fname, ep->entry->bb);

        FOR_EACH_PTR(ep->bbs, bb) {
                struct basic_block *child;
                int ret = 0;
                const char * s = ", ls=\"[";

                /* Node for the bb */
                printf("    bb%p [shape=ellipse,label=%d,line=%d,col=%d",
                       bb, bb->pos.line, bb->pos.line, bb->pos.pos);


                /* List loads and stores */
                FOR_EACH_PTR(bb->insns, insn) {
                        if (!insn->bb)
                                continue;
                        switch(insn->opcode) {
                        case OP_STORE:
                                if (insn->src->type == PSEUDO_SYM) {
                                  printf("%s store(%s)", s, show_ident(insn->src->sym->ident));
                                  s = ",";
                                }
                                break;

                        case OP_LOAD:
                                if (insn->src->type == PSEUDO_SYM) {
                                  printf("%s load(%s)", s, show_ident(insn->src->sym->ident));
                                  s = ",";
                                }
                                break;

                        case OP_RET:
                                ret = 1;
                                break;

                        }
                } END_FOR_EACH_PTR(insn);
                if (s[1] == 0)
                        printf("]\"");
                if (ret)
                        printf(",op=ret");
                printf("];\n");

                /* Edges between bbs; lower weight for upward edges */
                FOR_EACH_PTR(bb->children, child) {
                        printf("    bb%p -> bb%p [op=br, %s];\n", bb, child,
                               (bb->pos.line > child->pos.line) ? "weight=5" : "weight=10");
                } END_FOR_EACH_PTR(child);
        } END_FOR_EACH_PTR(bb);

        printf("}\n");
}


/* Insert edges for intra- or inter-file calls, depending on the value
 * of internal. Bold edges are used for calls with destinations;
 * dashed for calls to external functions */
static void graph_calls(struct entrypoint *ep, int internal)
{
        struct basic_block *bb;
        struct instruction *insn;

        show_ident(ep->name->ident);
        stream_name(ep->entry->bb->pos.stream);

        FOR_EACH_PTR(ep->bbs, bb) {
                if (!bb)
                        continue;
                if (!bb->parents && !bb->children && !bb->insns && verbose < 2)
                        continue;

                FOR_EACH_PTR(bb->insns, insn) {
                        if (!insn->bb)
                                continue;
                        if (insn->opcode == OP_CALL &&
                            internal == !(insn->func->sym->ctype.modifiers & MOD_EXTERN)) {

                                /* Find the symbol for the callee's definition */
                                struct symbol * sym;
                                if (insn->func->type == PSEUDO_SYM) {
                                        for (sym = insn->func->sym->ident->symbols;
                                             sym; sym = sym->next_id) {
                                                if (sym->namespace & NS_SYMBOL && sym->ep)
                                                        break;
                                        }

                                        if (sym)
                                                printf("bb%p -> bb%p"
                                                       "[label=%d,line=%d,col=%d,op=call,style=bold,weight=30];\n",
                                                       bb, sym->ep->entry->bb,
                                                       insn->pos.line, insn->pos.line, insn->pos.pos);
                                        else
                                                printf("bb%p -> \"%s\" "
                                                       "[label=%d,line=%d,col=%d,op=extern,style=dashed];\n",
                                                       bb, show_pseudo(insn->func),
                                                       insn->pos.line, insn->pos.line, insn->pos.pos);
                                }
                        }
                } END_FOR_EACH_PTR(insn);
        } END_FOR_EACH_PTR(bb);
}

int main(int argc, char **argv)
{
        struct string_list *filelist = NULL;
        char *file;
        struct symbol *sym;

        struct symbol_list *fsyms, *all_syms=NULL;

        printf("digraph call_graph {\n");
        fsyms = sparse_initialize(argc, argv, &filelist);
        concat_symbol_list(fsyms, &all_syms);

        /* Linearize all symbols, graph internal basic block
         * structures and intra-file calls */
        FOR_EACH_PTR(filelist, file) {

                fsyms = sparse(file);
                concat_symbol_list(fsyms, &all_syms);

                FOR_EACH_PTR(fsyms, sym) {
                        expand_symbol(sym);
                        linearize_symbol(sym);
                } END_FOR_EACH_PTR(sym);

                FOR_EACH_PTR(fsyms, sym) {
                        if (sym->ep) {
                                graph_ep(sym->ep);
                                graph_calls(sym->ep, 1);
                        }
                } END_FOR_EACH_PTR(sym);

        } END_FOR_EACH_PTR(file);

        /* Graph inter-file calls */
        FOR_EACH_PTR(all_syms, sym) {
                if (sym->ep)
                        graph_calls(sym->ep, 0);
        } END_FOR_EACH_PTR(sym);

        printf("}\n");
        return 0;
}