1/*
2 * Copyright (c) 1999-2001 Proofpoint, Inc. and its suppliers.
3 *	All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 *
9 * Contributed by Exactis.com, Inc.
10 *
11 */
12
13#include <sm/gen.h>
14SM_RCSID("@(#)$Id: timers.c,v 8.27 2013-11-22 20:51:57 ca Exp $")
15
16#if _FFR_TIMERS
17# include <sys/types.h>
18# include <sm/time.h>
19# include "sendmail.h"
20# include <sys/resource.h>	/* Must be after sendmail.h for NCR MP-RAS */
21
22static TIMER	BaseTimer;		/* current baseline */
23static int	NTimers;		/* current pointer into stack */
24static TIMER	*TimerStack[MAXTIMERSTACK];
25
26static void
27# ifdef __STDC__
28warntimer(const char *msg, ...)
29# else /* __STDC__ */
30warntimer(msg, va_alist)
31	const char *msg;
32	va_dcl
33# endif /* __STDC__ */
34{
35	char buf[MAXLINE];
36	SM_VA_LOCAL_DECL
37
38# if 0
39	if (!tTd(98, 30))
40		return;
41# endif /* 0 */
42	SM_VA_START(ap, msg);
43	(void) sm_vsnprintf(buf, sizeof(buf), msg, ap);
44	SM_VA_END(ap);
45	sm_syslog(LOG_NOTICE, CurEnv->e_id, "%s; e_timers=0x%lx",
46		  buf, (unsigned long) &CurEnv->e_timers);
47}
48
49static void
50zerotimer(ptimer)
51	TIMER *ptimer;
52{
53	memset(ptimer, '\0', sizeof(*ptimer));
54}
55
56static void
57addtimer(ta, tb)
58	TIMER *ta;
59	TIMER *tb;
60{
61	tb->ti_wall_sec += ta->ti_wall_sec;
62	tb->ti_wall_usec += ta->ti_wall_usec;
63	if (tb->ti_wall_usec > 1000000)
64	{
65		tb->ti_wall_sec++;
66		tb->ti_wall_usec -= 1000000;
67	}
68	tb->ti_cpu_sec += ta->ti_cpu_sec;
69	tb->ti_cpu_usec += ta->ti_cpu_usec;
70	if (tb->ti_cpu_usec > 1000000)
71	{
72		tb->ti_cpu_sec++;
73		tb->ti_cpu_usec -= 1000000;
74	}
75}
76
77static void
78subtimer(ta, tb)
79	TIMER *ta;
80	TIMER *tb;
81{
82	tb->ti_wall_sec -= ta->ti_wall_sec;
83	tb->ti_wall_usec -= ta->ti_wall_usec;
84	if (tb->ti_wall_usec < 0)
85	{
86		tb->ti_wall_sec--;
87		tb->ti_wall_usec += 1000000;
88	}
89	tb->ti_cpu_sec -= ta->ti_cpu_sec;
90	tb->ti_cpu_usec -= ta->ti_cpu_usec;
91	if (tb->ti_cpu_usec < 0)
92	{
93		tb->ti_cpu_sec--;
94		tb->ti_cpu_usec += 1000000;
95	}
96}
97
98static int
99getcurtimer(ptimer)
100	TIMER *ptimer;
101{
102	struct rusage ru;
103	struct timeval now;
104
105	if (getrusage(RUSAGE_SELF, &ru) < 0 || gettimeofday(&now, NULL) < 0)
106		return -1;
107	ptimer->ti_wall_sec = now.tv_sec;
108	ptimer->ti_wall_usec = now.tv_usec;
109	ptimer->ti_cpu_sec = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
110	ptimer->ti_cpu_usec = ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
111	if (ptimer->ti_cpu_usec > 1000000)
112	{
113		ptimer->ti_cpu_sec++;
114		ptimer->ti_cpu_usec -= 1000000;
115	}
116	return 0;
117}
118
119static void
120getinctimer(ptimer)
121	TIMER *ptimer;
122{
123	TIMER cur;
124
125	if (getcurtimer(&cur) < 0)
126	{
127		zerotimer(ptimer);
128		return;
129	}
130	if (BaseTimer.ti_wall_sec == 0)
131	{
132		/* first call */
133		memset(ptimer, '\0', sizeof(*ptimer));
134	}
135	else
136	{
137		*ptimer = cur;
138		subtimer(&BaseTimer, ptimer);
139	}
140	BaseTimer = cur;
141}
142
143void
144flushtimers()
145{
146	NTimers = 0;
147	(void) getcurtimer(&BaseTimer);
148}
149
150void
151pushtimer(ptimer)
152	TIMER *ptimer;
153{
154	int i;
155	int save_errno = errno;
156	TIMER incr;
157
158	/* find how much time has changed since last call */
159	getinctimer(&incr);
160
161	/* add that into the old timers */
162	i = NTimers;
163	if (i > MAXTIMERSTACK)
164		i = MAXTIMERSTACK;
165	while (--i >= 0)
166	{
167		addtimer(&incr, TimerStack[i]);
168		if (TimerStack[i] == ptimer)
169		{
170			warntimer("Timer@0x%lx already on stack, index=%d, NTimers=%d",
171				  (unsigned long) ptimer, i, NTimers);
172			errno = save_errno;
173			return;
174		}
175	}
176	errno = save_errno;
177
178	/* handle stack overflow */
179	if (NTimers >= MAXTIMERSTACK)
180		return;
181
182	/* now add the timer to the stack */
183	TimerStack[NTimers++] = ptimer;
184}
185
186void
187poptimer(ptimer)
188	TIMER *ptimer;
189{
190	int i;
191	int save_errno = errno;
192	TIMER incr;
193
194	/* find how much time has changed since last call */
195	getinctimer(&incr);
196
197	/* add that into the old timers */
198	i = NTimers;
199	if (i > MAXTIMERSTACK)
200		i = MAXTIMERSTACK;
201	while (--i >= 0)
202		addtimer(&incr, TimerStack[i]);
203
204	/* pop back to this timer */
205	for (i = 0; i < NTimers; i++)
206	{
207		if (TimerStack[i] == ptimer)
208			break;
209	}
210
211	if (i != NTimers - 1)
212		warntimer("poptimer: odd pop (timer=0x%lx, index=%d, NTimers=%d)",
213			  (unsigned long) ptimer, i, NTimers);
214	NTimers = i;
215
216	/* clean up and return */
217	errno = save_errno;
218}
219
220char *
221strtimer(ptimer)
222	TIMER *ptimer;
223{
224	static char buf[40];
225
226	(void) sm_snprintf(buf, sizeof(buf), "%ld.%06ldr/%ld.%06ldc",
227		ptimer->ti_wall_sec, ptimer->ti_wall_usec,
228		ptimer->ti_cpu_sec, ptimer->ti_cpu_usec);
229	return buf;
230}
231#endif /* _FFR_TIMERS */
232