1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27
28#include "Trace.h"
29#include <cstdarg>
30#include <string>
31#include <cstdio>
32#include <string>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <fcntl.h>
36#include <unistd.h>
37#include <cstring>
38
39using namespace std;
40
41/**
42 * Tracking for the stacks
43 */
44vector<vector<Trace *> > Trace::stacks;
45
46/**
47 * The indentation string for output
48 */
49vector<string> Trace::indent;
50
51#define MAX_MSG_PREFIX_LEN 128
52#define	CTIME_LEN	26
53#define	DEBUG_FILE	"/var/adm/sun_fc.debug"
54#define	LOG_FILE	"/var/adm/sun_fc"
55
56/**
57 * @memo	    Log a message
58 * @param	    priority The priority of the message (see syslog man page)
59 * @param	    msg The message string
60 *
61 * @doc		    If the debug file is present, we will log everything to
62 *		    that file.  Otherwise, if the normal log file is present,
63 *		    we'll log all non-debug messages to that file.  Lastly,
64 *		    if neither file is present, the message will be
65 *		    silently discarded.
66 */
67void Trace::message(int priority, const char *msg) {
68	char prefix[MAX_MSG_PREFIX_LEN];
69	char message[MAX_MSG_PREFIX_LEN + MAX_MSG_LEN + 2];
70	int fd;
71	// char time[CTIME_LEN+1];
72	std::string priString;
73
74
75	/* If we can open the log file, write there, else use the cim log */
76	fd = open(DEBUG_FILE, O_WRONLY|O_APPEND); /* will only open if exists */
77	if (fd == -1) {
78	    /* No debug file, how about the log file? */
79	    if (priority == LOG_DEBUG) {
80		return; /* Ignore debug */
81	    }
82	    fd = open(LOG_FILE, O_WRONLY|O_APPEND);
83	    /* We catch open failures later */
84	}
85
86	// now(time);
87	/* First interpret the priority value */
88	switch (priority) {
89	case INTERNAL_ERROR:
90	    priString = "INTERNAL";
91	    break;
92	case STACK_TRACE:
93	    priString = "STACK";
94	    break;
95	case IO_ERROR:
96	    priString = "IO";
97	    break;
98	case USER_ERROR:
99	    priString = "USER";
100	    break;
101	case LOG_DEBUG:
102	    priString = "DEBUG";
103	    break;
104	default:
105	    priString = "UNKNOWN";
106	    break;
107	}
108
109	if (fd != -1) {
110	    /* Format the prefix string for the log file */
111	    snprintf(prefix, MAX_MSG_PREFIX_LEN, "%d:%d:%s%s:%s",
112		time(NULL),
113		tid,
114		indent[tid].c_str(),
115		routine.c_str(),
116		priString.c_str());
117
118	    /* Format the message string for the log file */
119	    snprintf(message, strlen(prefix) + MAX_MSG_LEN + 2, "%s:%s\n",
120		prefix,
121		msg);
122	    write(fd, message, strlen(message));
123	    close(fd);
124	} /* Else discard the log message */
125}
126
127/**
128 * @memo	    Create a new Trace instance and update stack.
129 * @param	    myRoutine The name of the routine
130 *
131 * @doc		    This class was developed to provide a Java
132 *		    like stack trace capability, as C++ does not provide
133 *		    a run-time stack lookup feature.  Instances of the
134 *		    Trace class should be created on the stack so they
135 *		    will be automatically freed when the stack is popped
136 *		    when the function returns.
137 */
138Trace::Trace(std::string myRoutine) : routine(myRoutine) {
139	tid = pthread_self();
140	if (stacks.size() < tid+1) {
141	    stacks.resize(tid+1);
142	    indent.resize(tid+1);
143	    indent[tid] = "";
144	}
145	message(LOG_DEBUG, "entered");
146	stacks[tid].push_back(this);
147	indent[tid] += " ";
148}
149
150/**
151 * @memo	    Delete a trace instances and update the stack
152 */
153Trace::~Trace() {
154	string::size_type len = indent[tid].size();
155	if (len > 0) {
156	    indent[tid].resize(len - 1);
157	}
158	message(LOG_DEBUG, "exited");
159	stacks[tid].pop_back();
160}
161
162/**
163 * @memo	    Print out the current stack trace information
164 */
165void Trace::stackTrace() {
166	message(STACK_TRACE, "Stack trace follows");
167	for (vector<Trace *>::size_type i = stacks[tid].size() - 1; ; i--) {
168	    string msg = "	    ";
169	    msg += (stacks[tid])[i]->label();
170	    message(STACK_TRACE, msg.c_str());
171	    if (i == 0) {
172		break;
173	    }
174	}
175}
176