/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 (c) 1997-2000 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* interceptor.c -- a functional decomposition of generate.c,
* the code generator for apptrace
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include "parser.h"
#include "trace.h"
#include "util.h"
#include "db.h"
#include "symtab.h"
#include "io.h"
#include "bindings.h"
#include "printfuncs.h"
#include "errlog.h"
#include "parseproto.h"
static void generate_i_declarations(char *, int, char *);
static void generate_i_preamble(ENTRY *);
static void generate_i_call();
static int generate_i_bindings(int);
static void generate_i_postamble(ENTRY *, int, char *, char *);
static void generate_i_evaluations(ENTRY *);
static void generate_i_prints(ENTRY *, char *, char *);
static void generate_i_closedown(char *, int);
static void generate_i_live_vars(ENTRY *);
static void generate_return_printf(int);
static char *variables_get_errorname(void);
/*
* generate_interceptor -- make code for an individual interceptor, written
* as an output grammar
*/
void
generate_interceptor(ENTRY *function)
{
char *prototype = symtab_get_prototype(),
*library_name = db_get_current_library(),
*function_name,
*error_name;
int void_func;
errlog(BEGIN, "generate_interceptor() {");
/* Check for required information. */
if (validity_of(function) == NO) {
symtab_set_skip(YES);
errlog(WARNING|INPUT, "No prototype for interface, "
"it will be skipped");
errlog(END, "}");
return;
}
/* Collect things we'll use more than once. */
function_name = name_of(function);
error_name = variables_get_errorname();
void_func = is_void(function);
/*
* Emit "artificial" prototype here so that if there's a
* disagreement between it and the prototype contained in the
* declaring header, the compiler will flag it.
* First #undef the function to make sure the prototype in the header
* is exposed and to avoid breaking the artificial prototype if it's
* not.
*/
{
decl_t *dp;
char *buf;
char const *err;
size_t s;
s = strlen(prototype) + 2;
buf = malloc(s);
if (buf == NULL)
abort();
(void) strcpy(buf, prototype);
buf[s - 2] = ';';
buf[s - 1] = '\0';
err = decl_Parse(buf, &dp);
if (err != NULL)
errlog(FATAL, "\"%s\", line %d: %s: %s",
symtab_get_filename(), line_of(function),
err, prototype);
/* generate the mapfile entry */
(void) fprintf(Mapfp, "\t__abi_%s;\n", decl_GetName(dp));
(void) decl_ToString(buf, DTS_DECL, dp, function_name);
(void) fprintf(Bodyfp, "#line %d \"%s\"\n",
line_of(function), symtab_get_filename());
(void) fprintf(Bodyfp, "#undef %s\n", function_name);
(void) fprintf(Bodyfp, "extern %s;\n", buf);
(void) fprintf(Bodyfp, "static %s\n{\n", prototype);
(void) decl_ToString(buf, DTS_RET, dp, "_return");
generate_i_declarations(error_name, void_func, buf);
decl_Destroy(dp);
free(buf);
}
generate_i_preamble(function);
generate_i_call(function, void_func, library_name, error_name);
generate_i_postamble(function, void_func, error_name, library_name);
errlog(END, "}");
}
/*
* print_function_signature -- print the line defining the function, without
* an ``extern'' prefix or either a ``;'' or ''{'' suffix.
*/
void
print_function_signature(char *xtype, char *name, char *formals)
{
char buffer[MAXLINE];
(void) snprintf(buffer, sizeof (buffer), "%s", name);
(void) fprintf(Bodyfp, xtype, buffer);
if (strstr(xtype, "(*") == NULL) {
(void) fprintf(Bodyfp, "(%s)", formals);
}
}
/*
* generate_i_declarations -- generate the declarations which
* are local to the interceptor function itself.
*/
static void
generate_i_declarations(char *errname, int voidfunc, char *ret_str)
{
errlog(BEGIN, "generate_i_declarations() {");
if (*errname != NULL) {
/* Create locals for errno-type variable, */
(void) fprintf(Bodyfp,
" int saved_errvar = %s;\n", errname);
(void) fprintf(Bodyfp, " int functions_errvar;\n");
}
if (need_exception_binding()) {
/* Create a local for that. */
(void) fprintf(Bodyfp, " int exception = 0;\n");
}
if (! voidfunc) {
/* Create a return value. */
(void) fprintf(Bodyfp, " %s;\n", ret_str);
}
(void) fprintf(Bodyfp, " sigset_t omask;\n");
(void) putc('\n', Bodyfp);
errlog(END, "}");
}
/*
* generate_i_preamble -- do the actions which must occur
* before the call.
*/
static void
generate_i_preamble(ENTRY *function)
{
errlog(BEGIN, "generate_i_preamble() {");
generate_i_live_vars(function); /* Deferred. */
if (symtab_get_nonreturn() == YES) {
/* Make things safe for printing */
(void) fprintf(Bodyfp,
" abilock(&omask);\n");
/* Print all the args in terse format. */
generate_printf(function);
(void) fputs(" putc('\\n', ABISTREAM);\n\n", Bodyfp);
/* unlock stdio */
(void) fprintf(Bodyfp,
" abiunlock(&omask);\n");
}
errlog(END, "}");
}
/*
* generate_i_call -- implement the save/call/restore cycle
*/
static void
generate_i_call(
ENTRY *function,
int void_func,
char *library_name,
char *error_name)
{
char *function_name = name_of(function),
*function_cast = symtab_get_cast(),
*actual_args = symtab_get_actuals();
errlog(BEGIN, "generate_i_call() {");
/* Zero the error variable. */
if (*error_name != NULL) {
(void) fprintf(Bodyfp, " %s = 0;\n", error_name);
}
/* Then print the call itself. */
if (void_func) {
(void) fprintf(Bodyfp,
" (void) ABI_CALL_REAL(%s, %s, %s)(%s);\n",
library_name, function_name, function_cast, actual_args);
} else {
(void) fprintf(Bodyfp,
" _return = ABI_CALL_REAL(%s, %s, %s)(%s);\n",
library_name, function_name, function_cast, actual_args);
}
/* Then set the local copy of the error variable. */
if (*error_name != NULL) {
(void) fprintf(Bodyfp,
" functions_errvar = %s;\n", error_name);
}
(void) putc('\n', Bodyfp);
/* Make things safe for printing */
(void) fprintf(Bodyfp,
" abilock(&omask);\n");
errlog(END, "}");
}
/*
* generate_i_postamble -- do all the things which come
* after the call. In the case of apptrace, this is most of the work.
*/
static void
generate_i_postamble(ENTRY *function, int void_func,
char *error_name, char *library_name)
{
errlog(BEGIN, "generate_i_postamble() {");
if (symtab_get_nonreturn() == NO) {
/* Print all the args in terse format. */
generate_printf(function);
}
/* If it isn't supposed to return, and actually ends up here, */
/* we'd better be prepared to print all sorts of diagnostic stuff */
(void) putc('\n', Bodyfp);
if (generate_i_bindings(void_func) == YES) {
generate_return_printf(void_func);
}
generate_i_prints(function, library_name, name_of(function));
generate_i_evaluations(function); /* Deferred */
generate_i_closedown(error_name, void_func);
errlog(END, "}");
}
/*
* generate_i_bindings -- see about success and failure, so we can decide
* what to do next.
*/
static int
generate_i_bindings(int void_func)
{
ENTRY *e;
char *exception;
exception = ((e = symtab_get_exception()) != NULL)?
(name_of(e)? name_of(e): ""): "";
errlog(BEGIN, "generate_i_bindings() {");
if (void_func && bindings_exist()) {
/* To become a warning, as there are spec errors! TBD */
errlog(FATAL, "exception bindings found in a "
"void function");
} else if (void_func || need_bindings(exception) == NO) {
(void) fprintf(Bodyfp,
" (void) putc('\\n', ABISTREAM);\n");
(void) putc('\n', Bodyfp);
errlog(END, "}");
return (NO);
} else {
/*
* Then there is a return value, so we try to
* generate exception bindings
* and code to print errno on exception.
*/
if ((generate_bindings(exception)) != ANTONYMS) {
/* Generate code to cross-evaluate them. */
(void) fprintf(Bodyfp,
" if (!exception) {\n");
errlog(END, "}");
return (YES);
}
}
/* should not get here */
errlog(END, "}");
return (NO);
}
/*
* generate_return_printf -- print the return value and end the line
*/
static void
generate_return_printf(int void_func)
{
errlog(BEGIN, "generate_return_printf() {");
if (void_func) {
(void) fprintf(Bodyfp, " putc('\\n', ABISTREAM);\n");
errlog(END, "}");
return;
}
/* If its a non-void function there are bindings. */
(void) fprintf(Bodyfp,
"\t/* Just end the line */\n"
"\tputc('\\n', ABISTREAM);\n"
" }\n"
" else {\n"
" fprintf(ABISTREAM, \"%%s%%d (%%s)\\n\", errnostr, "
"functions_errvar, strerror((int)functions_errvar));\n"
" }\n\n");
errlog(END, "}");
}
/*
* generate_i_prints -- if we're doing the verbose stuff,
* generate verbose printouts of the variables.
*/
static void
generate_i_prints(ENTRY *function, char *lib, char *func)
{
ENTRY *e;
errlog(BEGIN, "generate_i_prints() {");
if ((e = symtab_get_first_arg()) != NULL || !is_void(e)) {
/* Then we have to generate code for verbose reports. */
(void) fprintf(Bodyfp, " if (ABI_VFLAG(%s, %s) != 0) {\n",
lib, func);
generate_printfunc_calls(function);
(void) fprintf(Bodyfp, " }\n");
}
(void) putc('\n', Bodyfp);
errlog(END, "}");
}
/*
* generate_i_closedown -- restore error variables and return.
*/
static void
generate_i_closedown(char *error_name, int void_func)
{
errlog(BEGIN, "generate_i_closedown() {");
/* unlock stdio */
(void) fprintf(Bodyfp,
" abiunlock(&omask);\n");
if (*error_name != NULL) {
/* Restore error variables. */
(void) fprintf(Bodyfp,
" %s = (functions_errvar == 0)? "
" saved_errvar: functions_errvar;\n",
error_name);
}
/* And return. */
(void) fprintf(Bodyfp,
" return%s;\n",
(void_func)? "": " _return");
(void) fprintf(Bodyfp, "}\n");
(void) putc('\n', Bodyfp);
errlog(END, "}");
}
/*
* generate_i_live_vars -- generate temps for any ``out''
* or ``inout'' variables in the function. Deferred.
*/
/*ARGSUSED*/
static void
generate_i_live_vars(ENTRY *function)
{
errlog(BEGIN, "generate_i_live_vars() {");
errlog(END, "}");
}
/*
* generate_i_evaluations -- generate evaluations for
* all the expressions. Deferred.
*/
/*ARGSUSED*/
static void
generate_i_evaluations(ENTRY *function)
{
errlog(BEGIN, "generate_i_evaluations() {");
errlog(END, "}");
}
static char *
variables_get_errorname(void)
{
return ("ABI_ERRNO");
}