Files
mercury/deep_profiler/timeout.m
Peter Wang 72f174b4e2 Don't print value of errno in MR_fatal_error.
The majority of calls to MR_fatal_error do not follow an operation that
sets errno, so printing out an error message unrelated to the reason for
the fatal error will lead to confusion. It can also cause test failures
if errno happens to be set to non-zero some time prior to an expected
call to MR_fatal_error. Fixes bug #464.

runtime/mercury_misc.c:
    Don't print value of errno in MR_fatal_error.

runtime/mercury_context.c:
runtime/mercury_thread.c:
    Pass strerror strings to MR_fatal_error where appropriate.

runtime/mercury_memory_zones.c:
runtime/mercury_memory_zones.h:
    Pass strerror strings to MR_fatal_error following failures of
    MR_protect_pages. Document that this assumes MR_protect_pages sets
    errno on error.

    Skip unnecessary call to sprintf before MR_fatal_error.

runtime/mercury_deep_profiling.c:
    Skip unnecessary call to sprintf before MR_fatal_error.

    Reduce size of some buffers.

runtime/mercury_overflow.c:
runtime/mercury_stack_trace.c:
    Pass a fixed format string to MR_fatal_error just in case
    the message string may contain percentage signs.

runtime/mercury_tabling.c:
    Skip unnecessary call to sprintf before MR_fatal_error.

deep_profiler/timeout.m:
library/thread.m:
mdbcomp/shared_utilities.m:
    Pass strerror strings to MR_fatal_error where appropriate.

trace/mercury_trace.c:
    Skip unnecessary call to sprintf before MR_fatal_error.

trace/mercury_trace_external.c:
    Pass a fixed format string to MR_fatal_error just in case.
2018-08-19 12:19:19 +10:00

815 lines
22 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2001-2002, 2004-2006, 2008 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: timeout.m.
% Author: zs.
%
% This module implements timeouts and cleanup for the deep profiler.
%
% The timeout design we use and its rationale are given in the file DESIGN.
%
% The cleanup system consists of an array of filenames. When the profiler
% creates a temporary file, it adds its name to the array; when it deletes
% the temporary file, it deletes its name from the array. When we get an
% unexpected signal, we clean up by deleting all the temporary files named
% in the array.
%
% We establish the exit action to clean up the files as soon as they are
% created, but we don't want the parent process after the fork to delete them
% while they are still in use by the child process. This is prevented by the
% boolean flag process_is_detached_server.
%
%---------------------------------------------------------------------------%
:- module timeout.
:- interface.
:- import_module bool.
:- import_module io.
%---------------------------------------------------------------------------%
% Add the given file name to the list of files to be cleaned up.
%
:- pred register_file_for_cleanup(string::in, io::di, io::uo) is det.
% Remove the given file name from the list of files to be cleaned up.
%
:- pred unregister_file_for_cleanup(string::in, io::di, io::uo) is det.
% Remove all file names from the list of files to be cleaned up.
%
:- pred unregister_all_files_for_cleanup(io::di, io::uo) is det.
% Delete all the files on the cleanup list.
%
:- pred delete_cleanup_files(io::di, io::uo) is det.
% Set up signal handlers for all the signals we can catch.
% The three strings specify the name of the mutex file, the name of the
% directory containing the `want' files, and the prefix of the names of
% the `want' files.
%
:- pred setup_signals(string::in, string::in, string::in,
io::di, io::uo) is det.
% Set up a timeout for the given number of minutes in the future.
%
:- pred setup_timeout(int::in, io::di, io::uo) is det.
% Get the lock on the named mutex file if the bool is `no'.
% (The mutex file exists iff some process holds the lock.)
% If the bool is `yes', meaning debugging is enabled, do nothing.
%
:- pred get_lock(bool::in, string::in, io::di, io::uo) is det.
% Release the lock on the named mutex file if the bool is `no'.
% (The mutex file exists iff some process holds the lock.)
% If the bool is `yes', meaning debugging is enabled, do nothing.
%
:- pred release_lock(bool::in, string::in,
io::di, io::uo) is det.
% Create the `want' file with the given name.
%
:- pred make_want_file(string::in, io::di, io::uo) is det.
% Delete the `want' file with the given name.
%
:- pred remove_want_file(string::in, io::di, io::uo) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
%---------------------------------------------------------------------------%
:- pragma foreign_decl("C",
"
#ifdef MR_DEEP_PROFILER_ENABLED
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> /* for O_CREAT, O_EXCL */
#include <signal.h> /* for signal numbers */
#include <unistd.h> /* for alarm() */
#include <stdio.h>
#include <errno.h> /* for EEXIST etc */
#include <dirent.h>
#include ""mercury_signal.h""
#define MP_MAX_CLEANUP_FILES 20 /* this should be plenty */
extern const char *MP_cleanup_files[MP_MAX_CLEANUP_FILES];
extern int MP_cleanup_file_next;
extern void MP_maybe_print_cleanup_files(const char *msg);
extern void MP_register_cleanup_file(const char *filename);
extern void MP_unregister_cleanup_file(const char *filename);
extern void MP_handle_fatal_exception(void *data);
extern void MP_delete_cleanup_files(void);
MR_NO_RETURN(extern void MP_delete_cleanup_files_and_exit_failure(
const char *signal_name));
extern int MP_timeout_seconds;
extern const char *MP_timeout_mutex_file;
extern const char *MP_timeout_want_dir;
extern const char *MP_timeout_want_prefix;
typedef struct
{
int MP_signum;
void (*MP_handler)(void);
} MP_sig_handler;
extern const MP_sig_handler MP_signal_structs[];
extern void MP_handle_timeout(void);
MR_NO_RETURN(extern void MP_handle_sig_term(void));
MR_NO_RETURN(extern void MP_handle_sig_hup(void));
MR_NO_RETURN(extern void MP_handle_sig_int(void));
MR_NO_RETURN(extern void MP_handle_sig_quit(void));
MR_NO_RETURN(extern void MP_handle_sig_ill(void));
MR_NO_RETURN(extern void MP_handle_sig_abrt(void));
MR_NO_RETURN(extern void MP_handle_sig_bus(void));
MR_NO_RETURN(extern void MP_handle_sig_fpe(void));
MR_NO_RETURN(extern void MP_handle_sig_segv(void));
MR_NO_RETURN(extern void MP_handle_sig_pipe(void));
extern MR_bool MP_do_try_get_lock(const char *mutex_file);
extern void MP_do_get_lock(const char *mutex_file);
extern void MP_do_release_lock(const char *mutex_file);
#endif
").
:- pragma foreign_code("C",
"
#ifdef MR_DEEP_PROFILER_ENABLED
#include <sys/types.h>
const char *MP_cleanup_files[MP_MAX_CLEANUP_FILES];
int MP_cleanup_file_next = 0;
int MP_timeout_seconds = 30 * 60;
const char *MP_timeout_mutex_file = NULL;
const char *MP_timeout_want_dir = NULL;
const char *MP_timeout_want_prefix = NULL;
/* set this variable to MR_TRUE to debug the code cleanup array */
MR_bool MP_print_cleanup_files = MR_FALSE;
void
MP_maybe_print_cleanup_files(const char *msg)
{
int i;
if (MP_print_cleanup_files) {
fprintf(stderr, ""\\n%s cleanup files:\\n"", msg);
for (i = 0; i < MP_cleanup_file_next; i++) {
fprintf(stderr, ""%i %s\\n"", i, MP_cleanup_files[i]);
}
}
}
void
MP_register_cleanup_file(const char *filename)
{
int i;
if (MP_cleanup_file_next >= MP_MAX_CLEANUP_FILES - 1) {
MR_fatal_error(""MP_register_cleanup_file: too many entries"");
}
for (i = 0; i < MP_cleanup_file_next; i++) {
if (MR_streq(filename, MP_cleanup_files[i])) {
MR_fatal_error(""MP_register_cleanup_file: duplicate"");
}
}
MP_cleanup_files[MP_cleanup_file_next] = filename;
MP_cleanup_file_next++;
MP_maybe_print_cleanup_files(""register"");
}
void
MP_unregister_cleanup_file(const char *filename)
{
int i;
int j;
for (i = 0; i < MP_cleanup_file_next; i++) {
if (MR_streq(filename, MP_cleanup_files[i])) {
/* shift the array entries above index i down one */
for (j = i + 1; j < MP_cleanup_file_next; j++) {
MP_cleanup_files[j - 1] = MP_cleanup_files[j];
}
MP_cleanup_file_next--;
MP_maybe_print_cleanup_files(""unregister"");
return;
}
}
MR_fatal_error(""MP_unregister_cleanup_file: not found"");
}
void
MP_handle_fatal_exception(void *data)
{
/* we ignore data */
MP_delete_cleanup_files();
}
void
MP_delete_cleanup_files(void)
{
int i;
MR_bool delayed_mutex_file;
/*
** We want to remove the mutex file only after we have removed the
** files manipulated by the critical section it was protecting.
*/
MP_maybe_print_cleanup_files(""delete"");
delayed_mutex_file = MR_FALSE;
for (i = 0; i < MP_cleanup_file_next; i++) {
if (MR_streq(MP_timeout_mutex_file, MP_cleanup_files[i])) {
delayed_mutex_file = MR_TRUE;
} else {
if (remove(MP_cleanup_files[i]) != 0) {
perror(MP_cleanup_files[i]);
}
}
}
if (delayed_mutex_file) {
if (remove(MP_timeout_mutex_file) != 0) {
perror(MP_timeout_mutex_file);
}
}
MP_cleanup_file_next = 0;
}
void
MP_delete_cleanup_files_and_exit_failure(const char *signal_name)
{
#ifdef MP_DEBUG_MDPROF_SIGNAL
FILE *fp;
char buf[1024]; /* that should be big enough */
fp = fopen(""/tmp/mdprof_signal"", ""w"");
if (fp != NULL) {
fprintf(fp, ""%s\\n"", signal_name);
(void) fclose(fp);
}
#endif
MP_delete_cleanup_files();
#ifdef MP_DEBUG_MDPROF_SIGNAL
sprintf(buf, ""Mercury deep profiler: received unexpected signal %s"",
signal_name);
MR_fatal_error(buf);
#else
exit(EXIT_FAILURE);
#endif
}
/*
** SIGALRM alarm signal indicates a timeout. SIGTERM usually indicates the
** machine is being shut down. The others are there to catch forceful shutdowns
** during development, both intentional ones where the programmer sends the
** signal and those caused by bugs in the server code. We would like to include
** all catchable, fatal signals in this list, but that set is somewhat OS
** dependent. The set whose existence we test for here includes all the
** signals that are at all likely to be sent to server process.
**
** We don't test for the existence of SIGALRM, because we want compilation to
** fail if it does not exist. Without alarm signals, server processes will
** never be timed out, and thus constitute a resource leak (mostly of virtual
** memory/swap space).
**
** We could avoid this problem if we had a version of atexit that executed
** its actions even when the program exits after a signal.
*/
const MP_sig_handler MP_signal_structs[] =
{
{ SIGALRM, MP_handle_timeout },
#ifdef SIGTERM
{ SIGTERM, MP_handle_sig_term },
#endif
#ifdef SIGHUP
{ SIGHUP, MP_handle_sig_hup },
#endif
#ifdef SIGINT
{ SIGINT, MP_handle_sig_int },
#endif
#ifdef SIGQUIT
{ SIGQUIT, MP_handle_sig_quit },
#endif
#ifdef SIGILL
{ SIGILL, MP_handle_sig_ill },
#endif
#ifdef SIGABRT
{ SIGABRT, MP_handle_sig_abrt },
#endif
#ifdef SIGBUS
{ SIGBUS, MP_handle_sig_bus },
#endif
#ifdef SIGFPE
{ SIGFPE, MP_handle_sig_fpe },
#endif
#ifdef SIGSEGV
{ SIGSEGV, MP_handle_sig_segv },
#endif
#ifdef SIGPIPE
{ SIGPIPE, MP_handle_sig_pipe },
#endif
{ -1, NULL }
};
void
MP_handle_timeout(void)
{
DIR *dir;
struct dirent *dirent;
size_t matchlen;
MR_bool success;
#ifdef MP_DEBUG_LOCKS
{
FILE *debug_fp;
debug_fp = fopen(""/tmp/deep_locks"", ""a"");
if (debug_fp != NULL) {
fprintf(debug_fp, ""pid %d MP_handle_timeout at %d\\n"",
getpid(), (int) time(NULL));
fclose(debug_fp);
}
}
#endif
if (MP_timeout_want_dir == NULL || MP_timeout_want_prefix == NULL) {
MR_fatal_error(""MP_handle_timeout: null dir or prefix"");
}
matchlen = strlen(MP_timeout_want_prefix);
success = MP_do_try_get_lock(MP_timeout_mutex_file);
if (! success) {
/*
** We could not get the lock, so some other process holds it.
** We therefore abort the timeout, but schedule the next one.
*/
#ifdef MP_DEBUG_LOCKS
{
FILE *debug_fp;
debug_fp = fopen(""/tmp/deep_locks"", ""a"");
if (debug_fp != NULL) {
fprintf(debug_fp, ""pid %d aborting timeout: lock\\n"",
getpid());
fclose(debug_fp);
}
}
#endif
(void) alarm(MP_timeout_seconds);
return;
}
dir = opendir(MP_timeout_want_dir);
if (dir == NULL) {
char errbuf[MR_STRERROR_BUF_SIZE];
MR_fatal_error(""MP_handle_timeout: opendir failed: %s"",
MR_strerror(errno, errbuf, sizeof(errbuf)));
}
while ((dirent = readdir(dir)) != NULL) {
if (MR_strneq(dirent->d_name, MP_timeout_want_prefix, matchlen)) {
#ifdef MP_DEBUG_LOCKS
{
FILE *debug_fp;
debug_fp = fopen(""/tmp/deep_locks"", ""a"");
if (debug_fp != NULL) {
fprintf(debug_fp,
""pid %d aborting timeout: want file\\n"", getpid());
fclose(debug_fp);
}
}
#endif
/* abort the timeout */
(void) closedir(dir);
(void) alarm(MP_timeout_seconds);
return;
}
}
(void) closedir(dir);
/*
** This call will delete the mutex file last, releasing the mutex.
*/
MP_delete_cleanup_files();
#ifdef MP_DEBUG_LOCKS
{
FILE *debug_fp;
debug_fp = fopen(""/tmp/deep_locks"", ""a"");
if (debug_fp != NULL) {
fprintf(debug_fp, ""pid %d timeout exit\\n"", getpid());
fclose(debug_fp);
}
}
#endif
exit(EXIT_SUCCESS);
}
void
MP_handle_sig_term(void)
{
MP_delete_cleanup_files_and_exit_failure(""SIGTERM"");
}
void
MP_handle_sig_hup(void)
{
MP_delete_cleanup_files_and_exit_failure(""SIGHUP"");
}
void
MP_handle_sig_int(void)
{
MP_delete_cleanup_files_and_exit_failure(""SIGINT"");
}
void
MP_handle_sig_quit(void)
{
MP_delete_cleanup_files_and_exit_failure(""SIGQUIT"");
}
void
MP_handle_sig_ill(void)
{
MP_delete_cleanup_files_and_exit_failure(""SIGILL"");
}
void
MP_handle_sig_abrt(void)
{
MP_delete_cleanup_files_and_exit_failure(""SIGABRT"");
}
void
MP_handle_sig_bus(void)
{
MP_delete_cleanup_files_and_exit_failure(""SIGBUS"");
}
void
MP_handle_sig_fpe(void)
{
MP_delete_cleanup_files_and_exit_failure(""SIGFPE"");
}
void
MP_handle_sig_segv(void)
{
MP_delete_cleanup_files_and_exit_failure(""SIGSEGV"");
}
void
MP_handle_sig_pipe(void)
{
MP_delete_cleanup_files_and_exit_failure(""SIGPIPE"");
}
MR_bool
MP_do_try_get_lock(const char *mutex_file)
{
int res;
MR_bool success;
res = open(mutex_file, O_CREAT | O_EXCL, 0);
if (res >= 0) {
#ifdef MP_DEBUG_LOCKS
FILE *debug_fp;
debug_fp = fopen(""/tmp/deep_locks"", ""a"");
if (debug_fp != NULL) {
fprintf(debug_fp, ""pid %d try: lock %s\\n"",
getpid(), mutex_file);
fclose(debug_fp);
}
#endif
(void) close(res);
MP_register_cleanup_file(mutex_file);
success = MR_TRUE;
} else if (res < 0 && errno == EEXIST) {
#ifdef MP_DEBUG_LOCKS
FILE *debug_fp;
debug_fp = fopen(""/tmp/deep_locks"", ""a"");
if (debug_fp != NULL) {
fprintf(debug_fp, ""pid %d try: no lock %s\\n"",
getpid(), mutex_file);
fclose(debug_fp);
}
#endif
success = MR_FALSE;
} else {
char errbuf[MR_STRERROR_BUF_SIZE];
MR_fatal_error(""MP_do_try_get_lock failed: %s"",
MR_strerror(errno, errbuf, sizeof(errbuf)));
}
return success;
}
void
MP_do_get_lock(const char *mutex_file)
{
int res;
for (;;) {
res = open(mutex_file, O_CREAT | O_EXCL, 0);
if (res >= 0) {
#ifdef MP_DEBUG_LOCKS
FILE *debug_fp;
debug_fp = fopen(""/tmp/deep_locks"", ""a"");
if (debug_fp != NULL) {
fprintf(debug_fp, ""pid %d got lock %s\\n"",
getpid(), mutex_file);
fclose(debug_fp);
}
#endif
(void) close(res);
MP_register_cleanup_file(mutex_file);
return;
} else if (res < 0 && errno == EEXIST) {
#ifdef MP_DEBUG_LOCKS
FILE *debug_fp;
debug_fp = fopen(""/tmp/deep_locks"", ""a"");
if (debug_fp != NULL) {
fprintf(debug_fp, ""pid %d trying to lock %s ...\\n"",
getpid(), mutex_file);
fclose(debug_fp);
}
#endif
sleep(5);
continue;
} else {
char errbuf[MR_STRERROR_BUF_SIZE];
MR_fatal_error(""MP_do_get_lock failed: %s"",
MR_strerror(errno, errbuf, sizeof(errbuf)));
}
}
}
void
MP_do_release_lock(const char *mutex_file)
{
#ifdef MP_DEBUG_LOCKS
FILE *debug_fp;
debug_fp = fopen(""/tmp/deep_locks"", ""a"");
if (debug_fp != NULL) {
fprintf(debug_fp, ""pid %d releasing lock %s\\n"",
getpid(), mutex_file);
fclose(debug_fp);
}
#endif
MP_unregister_cleanup_file(mutex_file);
(void) unlink(mutex_file);
}
#endif /* MR_DEEP_PROFILER_ENABLED */
").
:- pragma foreign_proc("C",
register_file_for_cleanup(File::in, _S0::di, _S::uo),
[will_not_call_mercury, promise_pure],
"
#ifdef MR_DEEP_PROFILER_ENABLED
MP_register_cleanup_file(File);
#else
MR_fatal_error(""deep profiler not enabled"");
#endif
").
:- pragma foreign_proc("C",
unregister_file_for_cleanup(File::in, _S0::di, _S::uo),
[will_not_call_mercury, promise_pure],
"
#ifdef MR_DEEP_PROFILER_ENABLED
MP_unregister_cleanup_file(File);
#else
MR_fatal_error(""deep profiler not enabled"");
#endif
").
:- pragma foreign_proc("C",
unregister_all_files_for_cleanup(_S0::di, _S::uo),
[will_not_call_mercury, promise_pure],
"
#ifdef MR_DEEP_PROFILER_ENABLED
MP_cleanup_file_next = 0;
#else
MR_fatal_error(""deep profiler not enabled"");
#endif
").
:- pragma foreign_proc("C",
delete_cleanup_files(_S0::di, _S::uo),
[will_not_call_mercury, promise_pure],
"
#ifdef MR_DEEP_PROFILER_ENABLED
MP_delete_cleanup_files();
#else
MR_fatal_error(""deep profiler not enabled"");
#endif
").
:- pragma foreign_proc("C",
setup_signals(MutexFile::in, WantDir::in, WantPrefix::in, _S0::di, _S::uo),
[will_not_call_mercury, promise_pure],
"
#ifdef MR_DEEP_PROFILER_ENABLED
int i;
#ifdef MP_DEBUG_LOCKS
{
FILE *debug_fp;
debug_fp = fopen(""/tmp/deep_locks"", ""a"");
if (debug_fp != NULL) {
fprintf(debug_fp, ""pid %d setup signals ...\\n"", getpid());
fprintf(debug_fp, ""mutexfile %s, wantdir %s, wantprefix %s\\n"",
MutexFile, WantDir, WantPrefix);
fclose(debug_fp);
}
}
#endif
MP_timeout_mutex_file = MutexFile;
MP_timeout_want_dir = WantDir;
MP_timeout_want_prefix = WantPrefix;
for (i = 0; MP_signal_structs[i].MP_signum >= 0; i++) {
MR_setup_signal(MP_signal_structs[i].MP_signum,
MP_signal_structs[i].MP_handler, MR_FALSE,
""Mercury deep profiler: cannot setup signal exit"");
}
/*
** Mercury exceptions do not cause signals. The default exception
** handler prints and error message and exits. To ensure that
** we delete the files we need to clean up, we get the exit
** library function to invoke MP_delete_cleanup_files through
** MP_handle_fatal_exception.
*/
MR_register_exception_cleanup(MP_handle_fatal_exception, NULL);
#else
MR_fatal_error(""deep profiler not enabled"");
#endif
").
:- pragma foreign_proc("C",
setup_timeout(Minutes::in, _S0::di, _S::uo),
[will_not_call_mercury, promise_pure],
"
#ifdef MR_DEEP_PROFILER_ENABLED
MP_timeout_seconds = (int) Minutes * 60;
(void) alarm(MP_timeout_seconds);
#ifdef MP_DEBUG_LOCKS
{
FILE *debug_fp;
debug_fp = fopen(""/tmp/deep_locks"", ""a"");
if (debug_fp != NULL) {
fprintf(debug_fp, ""pid %d setup timeout at %d for %d\\n"",
getpid(), (int) time(NULL),
(int) time(NULL) + MP_timeout_seconds);
fclose(debug_fp);
}
}
#endif
#else
MR_fatal_error(""deep profiler not enabled"");
#endif
").
%---------------------------------------------------------------------------%
get_lock(Debug, MutexFile, !IO) :-
(
Debug = yes
;
Debug = no,
do_get_lock(MutexFile, !IO)
).
release_lock(Debug, MutexFile, !IO) :-
(
Debug = yes
;
Debug = no,
do_release_lock(MutexFile, !IO)
).
:- pred do_get_lock(string::in, io::di, io::uo) is det.
:- pragma foreign_proc("C",
do_get_lock(MutexFile::in, _S0::di, _S::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
#ifdef MR_DEEP_PROFILER_ENABLED
MP_do_get_lock(MutexFile);
#else
MR_fatal_error(""deep profiler not enabled"");
#endif
").
:- pred do_release_lock(string::in, io::di, io::uo) is det.
:- pragma foreign_proc("C",
do_release_lock(MutexFile::in, _S0::di, _S::uo),
[will_not_call_mercury, promise_pure, tabled_for_io],
"
#ifdef MR_DEEP_PROFILER_ENABLED
MP_do_release_lock(MutexFile);
#else
MR_fatal_error(""deep profiler not enabled"");
#endif
").
:- pragma foreign_proc("C",
make_want_file(WantFileName::in, _S0::di, _S::uo),
[will_not_call_mercury, promise_pure],
"
#ifdef MR_DEEP_PROFILER_ENABLED
int fd;
char errbuf[MR_STRERROR_BUF_SIZE];
fd = open(WantFileName, O_CREAT, 0);
if (fd < 0) {
MR_fatal_error(""make_want_file: open failed: %s"",
MR_strerror(errno, errbuf, sizeof(errbuf)));
}
(void) close(fd);
MP_register_cleanup_file(WantFileName);
#else
MR_fatal_error(""deep profiler not enabled"");
#endif
").
:- pragma foreign_proc("C",
remove_want_file(WantFileName::in, _S0::di, _S::uo),
[will_not_call_mercury, promise_pure],
"
#ifdef MR_DEEP_PROFILER_ENABLED
MP_unregister_cleanup_file(WantFileName);
(void) unlink(WantFileName);
#else
MR_fatal_error(""deep profiler not enabled"");
#endif
").
%---------------------------------------------------------------------------%
:- end_module timeout.
%---------------------------------------------------------------------------%