# ENABLE_DISABLE_FSYNC=yes
+#------------------------------------------------------------------------------
+# For development, add this to include code to time various stages and report.
+# CFLAGS += -DMEASURE_TIMING
+
# End of EDITME for Exim 4.
smtp_input = TRUE;
+#ifdef MEASURE_TIMING
+report_time_since(×tamp_startup, US"daemon loop start"); /* testcase 0022 */
+#endif
+
/* Enter the never-ending loop... */
for (;;)
-void
-timesince(struct timeval * diff, struct timeval * then)
-{
-gettimeofday(diff, NULL);
-diff->tv_sec -= then->tv_sec;
-if ((diff->tv_usec -= then->tv_usec) < 0)
- {
- diff->tv_sec--;
- diff->tv_usec += 1000*1000;
- }
-}
-
-
-
-uschar *
-string_timediff(struct timeval * diff)
-{
-static uschar buf[sizeof("0.000s")];
-
-if (diff->tv_sec >= 5 || !LOGGING(millisec))
- return readconf_printtime((int)diff->tv_sec);
-
-sprintf(CS buf, "%u.%03us", (uint)diff->tv_sec, (uint)diff->tv_usec/1000);
-return buf;
-}
-
-
-uschar *
-string_timesince(struct timeval * then)
-{
-struct timeval diff;
-
-timesince(&diff, then);
-return string_timediff(&diff);
-}
-
/******************************************************************************/
/* In the test harness, wait just a bit to let the subprocess finish off
any debug output etc first. */
- if (f.running_in_test_harness) millisleep(300);
+ testharness_pause_ms(300);
DEBUG(D_deliver) debug_printf("journalling %s", big_buffer);
len = Ustrlen(big_buffer);
newly created process get going before we create another process. This should
ensure repeatability in the tests. We only need to wait a tad. */
- else if (f.running_in_test_harness) millisleep(500);
+ else testharness_pause_ms(500);
continue;
open_db dbblock;
open_db *dbm_file;
extern int acl_where;
+uschar *info;
-uschar *info = queue_run_pid == (pid_t)0
+#ifdef MEASURE_TIMING
+report_time_since(×tamp_startup, US"delivery start"); /* testcase 0022, 2100 */
+#endif
+
+info = queue_run_pid == (pid_t)0
? string_sprintf("delivering %s", id)
: string_sprintf("delivering %s (queue run pid %d)", id, queue_run_pid);
/* In the test harness, let the child do it's thing first. */
- if (f.running_in_test_harness) millisleep(500);
+ testharness_pause_ms(500);
/* If the process failed, there was some disaster in setting up the
error message. Unless the message is very old, ensure that addr_defer
(void)close(deliver_datafile);
deliver_datafile = -1;
DEBUG(D_deliver) debug_printf("end delivery of %s\n", id);
+#ifdef MEASURE_TIMING
+report_time_since(×tamp_startup, US"delivery end"); /* testcase 0005 */
+#endif
/* It is unlikely that there will be any cached resources, since they are
released after routing, and in the delivery subprocesses. However, it's
else if (pid == 0) /* child: fork again to totally disconnect */
{
- if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
+ testharness_pause_ms(100); /* let parent debug out */
/* does not return */
smtp_proxy_tls(cutthrough.cctx.tls_ctx, big_buffer, big_buffer_size,
pfd, 5*60);
extern char **environ;
+#ifdef MEASURE_TIMING
+(void)gettimeofday(×tamp_startup, NULL);
+#endif
+
/* If the Exim user and/or group and/or the configuration file owner/group were
defined by ref:name at build time, we must now find the actual uid/gid values.
This is a feature to make the lives of binary distributors easier. */
exim_fail("exim: getsockname() failed after -MC option: %s\n",
strerror(errno));
- if (f.running_in_test_harness) millisleep(500);
+ testharness_pause_ms(500);
break;
}
debug_file = stderr;
debug_fd = fileno(debug_file);
f.background_daemon = FALSE;
- if (f.running_in_test_harness) millisleep(100); /* lets caller finish */
+ testharness_pause_ms(100); /* lets caller finish */
if (debug_selector != D_v) /* -v only doesn't show this */
{
debug_printf("Exim version %s uid=%ld gid=%ld pid=%d D=%x\n",
issues (currently about tls_advertise_hosts and keep_environment not being
defined) */
-readconf_main(checking || list_options);
+ {
+#ifdef MEASURE_TIMING
+ struct timeval t0, diff;
+ (void)gettimeofday(&t0, NULL);
+#endif
+
+ readconf_main(checking || list_options);
+
+#ifdef MEASURE_TIMING
+ report_time_since(&t0, US"readconf_main (delta)");
+#endif
+ }
/* Now in directory "/" */
Now, since the intro of the ${acl } expansion, ACL definitions may be
needed in transports so we lost the optimisation. */
-readconf_rest();
+ {
+#ifdef MEASURE_TIMING
+ struct timeval t0, diff;
+ (void)gettimeofday(&t0, NULL);
+#endif
+
+ readconf_rest();
+
+#ifdef MEASURE_TIMING
+ report_time_since(&t0, US"readconf_rest (delta)");
+#endif
+ }
/* Handle the -brt option. This is for checking out retry configurations.
The next three arguments are a domain name or a complete address, and
/* Initialise subsystems as required */
#ifndef DISABLE_DKIM
-dkim_exim_init();
+ {
+# ifdef MEASURE_TIMING
+ struct timeval t0;
+ gettimeofday(&t0, NULL);
+# endif
+ dkim_exim_init();
+# ifdef MEASURE_TIMING
+ report_time_since(&t0, US"dkim_exim_init (delta)");
+# endif
+ }
#endif
-deliver_init();
+
+ {
+#ifdef MEASURE_TIMING
+ struct timeval t0;
+ gettimeofday(&t0, NULL);
+#endif
+ deliver_init();
+#ifdef MEASURE_TIMING
+ report_time_since(&t0, US"deliver_init (delta)");
+#endif
+ }
/* Handle a request to deliver one or more messages that are already on the
#endif
/* Allow sequencing of test actions */
- if (f.running_in_test_harness) millisleep(100);
+ testharness_pause_ms(100);
/* Write the request string, if not empty or already done */
if (!do_tls && do_shutdown) shutdown(cctx.sock, SHUT_WR);
#endif
- if (f.running_in_test_harness) millisleep(100);
+ testharness_pause_ms(100);
/* Now we need to read from the socket, under a timeout. The function
that reads a file can be used. */
#ifndef _FUNCTIONS_H_
#define _FUNCTIONS_H_
+#include <sys/time.h>
+
#ifdef EXIM_PERL
extern gstring *call_perl_cat(gstring *, uschar **, uschar *,
extern uschar *string_nextinlist(const uschar **, int *, uschar *, int);
extern const uschar *string_printing2(const uschar *, BOOL);
extern uschar *string_split_message(uschar *);
-extern uschar *string_timediff(struct timeval *);
-extern uschar *string_timesince(struct timeval *);
extern uschar *string_unprinting(uschar *);
#ifdef SUPPORT_I18N
extern uschar *string_address_utf8_to_alabel(const uschar *, uschar **);
#ifdef EXIM_TFO_PROBE
extern void tfo_probe(void);
#endif
-extern void timesince(struct timeval * diff, struct timeval * then);
extern void tls_modify_variables(tls_support *);
extern uschar *tod_stamp(int);
}
/******************************************************************************/
+static inline void
+timesince(struct timeval * diff, struct timeval * then)
+{
+gettimeofday(diff, NULL);
+diff->tv_sec -= then->tv_sec;
+if ((diff->tv_usec -= then->tv_usec) < 0)
+ {
+ diff->tv_sec--;
+ diff->tv_usec += 1000*1000;
+ }
+}
+
+static inline uschar *
+string_timediff(struct timeval * diff)
+{
+static uschar buf[sizeof("0.000s")];
+
+if (diff->tv_sec >= 5 || !LOGGING(millisec))
+ return readconf_printtime((int)diff->tv_sec);
+
+sprintf(CS buf, "%u.%03us", (uint)diff->tv_sec, (uint)diff->tv_usec/1000);
+return buf;
+}
+
+
+static inline uschar *
+string_timesince(struct timeval * then)
+{
+struct timeval diff;
+timesince(&diff, then);
+return string_timediff(&diff);
+}
+
+static inline void
+report_time_since(struct timeval * t0, uschar * where)
+{
+# ifdef MEASURE_TIMING
+struct timeval diff;
+timesince(&diff, t0);
+fprintf(stderr, "%d %s:\t%ld.%06ld\n",
+ (uint)getpid(), where, (long)diff.tv_sec, (long)diff.tv_usec);
+# endif
+}
+
+
+static inline void
+testharness_pause_ms(int millisec)
+{
+#ifndef MEASURE_TIMING
+if (f.running_in_test_harness) millisleep(millisec);
+#endif
+}
+
#endif /* !MACRO_PREDEF */
#endif /* _FUNCTIONS_H_ */
int test_harness_load_avg = 0;
int thismessage_size_limit = 0;
int timeout_frozen_after = 0;
+#ifdef MEASURE_TIMING
+struct timeval timestamp_startup;
+#endif
transport_instance *transports = NULL;
extern int test_harness_load_avg; /* For use when testing */
extern int thismessage_size_limit; /* Limit for this message */
extern int timeout_frozen_after; /* Max time to keep frozen messages */
+#ifdef MEASURE_TIMING
+extern struct timeval timestamp_startup; /* For development measurements */
+#endif
extern uschar *transport_name; /* Name of transport last started */
extern int transport_count; /* Count of bytes transported */
int subcount = 0;
uschar subdirs[64];
+#ifdef MEASURE_TIMING
+report_time_since(×tamp_startup, US"queue_run start");
+#endif
+
/* Cancel any specific queue domains. Turn off the flag that causes SMTP
deliveries not to happen, unless doing a 2-stage queue run, when the SMTP flag
gets set. Save the queue_runner's pid and the flag that indicates any
set_process_info("running queue: %s", fq->text);
fq->text[SPOOL_NAME_LENGTH-2] = 0;
+#ifdef MEASURE_TIMING
+ report_time_since(×tamp_startup, US"queue msg selected");
+#endif
+
if ((pid = fork()) == 0)
{
int rc;
- if (f.running_in_test_harness) millisleep(100);
+ testharness_pause_ms(100);
(void)close(pfd[pipe_read]);
rc = deliver_message(fq->text, force_delivery, FALSE);
exim_underbar_exit(rc == DELIVER_NOT_ATTEMPTED);
/* When testing, let the subprocess get going */
-if (f.running_in_test_harness) millisleep(250);
+testharness_pause_ms(250);
DEBUG(D_transport)
debug_printf("process %d writing to transport filter\n", (int)write_pid);
DEBUG(D_transport) debug_printf("transport_pass_socket succeeded (final-pid %d)\n", pid);
_exit(EXIT_SUCCESS);
}
- if (f.running_in_test_harness) sleep(1);
+ testharness_pause_ms(1000);
transport_do_pass_socket(transport_name, hostname, hostaddress,
id, socket_fd);
ignore all writing errors. (When in the test harness, we do do a short sleep so
any debugging output is likely to be in the same order.) */
-if (f.running_in_test_harness) millisleep(500);
+testharness_pause_ms(500);
DEBUG(D_transport) debug_printf("Writing message to pipe\n");
_exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
-if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
+testharness_pause_ms(100); /* let parent debug out */
set_process_info("proxying TLS connection for continued transport");
FD_ZERO(&rfds);
FD_SET(tls_out.active.sock, &rfds);
}
done:
- if (f.running_in_test_harness) millisleep(100); /* let logging complete */
+ testharness_pause_ms(100); /* let logging complete */
exim_exit(0, US"TLS proxy");
}
#endif
transport_count = 0;
#ifndef DISABLE_DKIM
+ {
+# ifdef MEASURE_TIMING
+ struct timeval t0;
+ gettimeofday(&t0, NULL);
+# endif
dkim_exim_sign_init();
# ifdef EXPERIMENTAL_ARC
{
}
}
# endif
+# ifdef MEASURE_TIMING
+ report_time_since(&t0, US"dkim_exim_sign_init (delta)");
+# endif
+ }
sx.ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message);
#else
sx.ok = transport_write_message(&tctx, 0);
int pid = fork();
if (pid == 0) /* child; fork again to disconnect totally */
{
- if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
+ testharness_pause_ms(100); /* let parent debug out */
/* does not return */
smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd,
ob->command_timeout);
if (sx.send_quit)
{
shutdown(sx.cctx.sock, SHUT_WR);
- millisleep(f.running_in_test_harness ? 200 : 20);
+ millisleep(20);
+ testharness_pause_ms(200);
if (fcntl(sx.cctx.sock, F_SETFL, O_NONBLOCK) == 0)
for (int i = 16; read(sx.cctx.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && i > 0;)
i--; /* drain socket */
ob->hosts_max_try_hardlimit);
}
- if (f.running_in_test_harness) millisleep(500); /* let server debug out */
+ testharness_pause_ms(500); /* let server debug out */
} /* End of loop for trying multiple hosts. */
/* If we failed to find a matching host in the list, for an already-open