OpenSolaris_b135/lib/sun_fc/common/Trace.cc

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */



#include "Trace.h"
#include <cstdarg>
#include <string>
#include <cstdio>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

using namespace std;

/**
 * Tracking for the stacks
 */
vector<vector<Trace *> > Trace::stacks;

/**
 * The indentation string for output
 */
vector<string> Trace::indent;

#define MAX_MSG_PREFIX_LEN 128
#define	CTIME_LEN	26
#define	DEBUG_FILE	"/var/adm/sun_fc.debug"
#define	LOG_FILE	"/var/adm/sun_fc"

/**
 * @memo	    Log a message
 * @param	    priority The priority of the message (see syslog man page)
 * @param	    msg The message string
 * 
 * @doc		    If the debug file is present, we will log everything to
 *		    that file.  Otherwise, if the normal log file is present,
 *		    we'll log all non-debug messages to that file.  Lastly,
 *		    if neither file is present, the message will be
 *		    silently discarded.
 */
void Trace::message(int priority, const char *msg) {
	char prefix[MAX_MSG_PREFIX_LEN];
	char message[MAX_MSG_PREFIX_LEN + MAX_MSG_LEN + 2];
	int fd;
	// char time[CTIME_LEN+1];
	std::string priString;


	/* If we can open the log file, write there, else use the cim log */
	fd = open(DEBUG_FILE, O_WRONLY|O_APPEND); /* will only open if exists */
	if (fd == -1) {
	    /* No debug file, how about the log file? */
	    if (priority == LOG_DEBUG) {
		return; /* Ignore debug */
	    }
	    fd = open(LOG_FILE, O_WRONLY|O_APPEND);
	    /* We catch open failures later */
	}

	// now(time);
	/* First interpret the priority value */
	switch (priority) {
	case INTERNAL_ERROR:
	    priString = "INTERNAL";
	    break;
	case STACK_TRACE:
	    priString = "STACK";
	    break;
	case IO_ERROR:
	    priString = "IO";
	    break;
	case USER_ERROR:
	    priString = "USER";
	    break;
	case LOG_DEBUG:
	    priString = "DEBUG";
	    break;
	default:
	    priString = "UNKNOWN";
	    break;
	}

	if (fd != -1) {
	    /* Format the prefix string for the log file */
	    snprintf(prefix, MAX_MSG_PREFIX_LEN, "%d:%d:%s%s:%s",
		time(NULL),
		tid,
		indent[tid].c_str(),
		routine.c_str(),
		priString.c_str());

	    /* Format the message string for the log file */
	    snprintf(message, strlen(prefix) + MAX_MSG_LEN + 2, "%s:%s\n",
		prefix,
		msg);
	    write(fd, message, strlen(message));
	    close(fd);
	} /* Else discard the log message */
}

/**
 * @memo	    Create a new Trace instance and update stack.
 * @param	    myRoutine The name of the routine 
 * 
 * @doc		    This class was developed to provide a Java
 *		    like stack trace capability, as C++ does not provide
 *		    a run-time stack lookup feature.  Instances of the
 *		    Trace class should be created on the stack so they
 *		    will be automatically freed when the stack is popped
 *		    when the function returns.
 */
Trace::Trace(std::string myRoutine) : routine(myRoutine) {
	tid = pthread_self();
	if (stacks.size() < tid+1) {
	    stacks.resize(tid+1);
	    indent.resize(tid+1);
	    indent[tid] = "";
	}
	message(LOG_DEBUG, "entered");
	stacks[tid].push_back(this);
	indent[tid] += " ";
}

/**
 * @memo	    Delete a trace instances and update the stack
 */
Trace::~Trace() {
	string::size_type len = indent[tid].size();
	if (len > 0) {
	    indent[tid].resize(len - 1);
	}
	message(LOG_DEBUG, "exited");
	stacks[tid].pop_back();
}

/**
 * @memo	    Print out the current stack trace information
 */
void Trace::stackTrace() {
	message(STACK_TRACE, "Stack trace follows");
	for (vector<Trace *>::size_type i = stacks[tid].size() - 1; ; i--) {
	    string msg = "	    ";
	    msg += (stacks[tid])[i]->label();
	    message(STACK_TRACE, msg.c_str());
	    if (i == 0) {
		break;
	    }
	}
}