tools/time.c
author Petr Nyc <Petr.Nyc@Oracle.COM>
Wed, 19 Mar 2014 06:01:55 -0700
branchs11u1-sru
changeset 2990 946e9428cb03
parent 16 33aaaec59991
child 998 3f6ed23e8aae
permissions -rw-r--r--
Added tag 0.175.1.18.0.3.0, S11.1SRU18.3 for changeset 83fae5048c6c

/*
 * 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 (c) 2010, Oracle and/or it's affiliates.  All rights reserved.
 */

/*
 * This compiles to a module that can be preloaded during a build.  If this
 * is preloaded, it interposes on time(2), gettimeofday(3C), and
 * clock_gethrtime(3C) and returns a constant number of seconds since epoch
 * when the execname matches one of the desired "programs" and TIME_CONSTANT
 * contains an integer value to be returned.
 */

#include <stdlib.h>
#include <ucontext.h>
#include <dlfcn.h>
#include <strings.h>
#include <time.h>

/* The list of programs that we want to use a constant time. */
static char *programs[] = { "autogen", "bash", "cpp", "cc1", "date", "doxygen",
	"erl", "javadoc", "ksh", "ksh93", "ld", "perl", "perl5.8.4", "perl5.10",
	"ruby", "sh", NULL };

static int
stack_info(uintptr_t pc, int signo, void *arg)
{
	Dl_info info;
	void *sym;

	if (dladdr1((void *)pc, &info, &sym, RTLD_DL_SYMENT) != NULL) {
		if (strstr(info.dli_fname, ".so") == NULL)
			*(char **)arg = (char *)info.dli_fname;
	}

	return (0);
}

static char *
my_execname()
{
	static char *execname;

	if (execname == NULL) {
		ucontext_t ctx;

		if (getcontext(&ctx) == 0)
			walkcontext(&ctx, stack_info, &execname);

		if (execname != NULL) {
			char *s = strrchr(execname, '/');

			if (s != NULL)
				execname = ++s;
		}
	}

	return (execname);
}

static time_t
time_constant()
{
	char *execname = my_execname();
	time_t result = -1;

	if (execname != NULL) {
		int i;

		for (i = 0; programs[i] != NULL; i++)
			if (strcmp(execname, programs[i]) == 0) {
				static char *time_string;

				if (time_string == NULL)
					time_string = getenv("TIME_CONSTANT");

				if (time_string != NULL)
					result = atoll(time_string);

				break;
			}
	}

	return (result);
}

time_t
time(time_t *ptr)
{
	time_t result = time_constant();

	if (result == (time_t)-1) {
		static time_t (*fptr)(time_t *);

		if (fptr == NULL)
			fptr = (time_t (*)(time_t *))dlsym(RTLD_NEXT, "time");

		result = (fptr)(ptr);
	} else if (ptr != NULL)
			*ptr = result;

	return (result);
}

int
gettimeofday(struct timeval *tp, void *tzp)
{
	static int (*fptr)(struct timeval *, void *);
	int result = -1;

	if (fptr == NULL)
		fptr = (int (*)(struct timeval *, void *))dlsym(RTLD_NEXT,
				"gettimeofday");

	if ((result = (fptr)(tp, tzp)) == 0) {
		time_t curtime = time_constant();

		if (curtime != (time_t)-1)
			tp->tv_sec = curtime;
	}

	return (result);
}

int
clock_gettime(clockid_t clock_id, struct timespec *tp)
{
	static int (*fptr)(clockid_t, struct timespec *);
	int result = -1;

	if (fptr == NULL)
		fptr = (int (*)(clockid_t, struct timespec *))dlsym(RTLD_NEXT,
				"clock_gettime");

	if ((result = (fptr)(clock_id, tp)) == 0) {
		time_t curtime = time_constant();

		if (curtime != (time_t)-1)
			tp->tv_sec = curtime;
	}

	return (result);
}