[m-rev.] diff 1/2: Rename ThreadScope files in runtime/
Paul Bone
paul at bone.id.au
Fri May 30 16:48:47 AEST 2014
Branches: master
---
Rename ThreadScope files in runtime/
ThreadScope is a tool for profiling the runtime of a parallel Haskell
(specifically GHC) program. The project initially promised to use an
extensible file format and since Mercury's parallel runtime system uses a
similar set of objects, Engines, Contexts and Sparks, it was a good idea to
use the same file format and then be able to use the graphical viewer
developed by the Haskell team.
As the project developed there were a number of issues maintaining this
compatibility. The main issue is that the eventlog file format was not
extensible enough. Therefore we've decided to break compatibility with
ThreadScope and develop our own file format and tools.
To avoid confusion we are renaming the term "ThreadScope" to to "Parallel
Profiling".
This change renames the mercury_threadscope.[ch] files in the runtime.
runtime/mercury_threadscope.c:
runtime/mercury_threadscope.h:
Old file names.
runtime/mercury_par_profile.c:
runtime/mercury_par_profile.h:
New file names.
runtime/Mmakefile:
runtime/mercury_context.c:
runtime/mercury_engine.c:
runtime/mercury_memory_handlers.c:
runtime/mercury_par_builtin.h:
runtime/mercury_thread.c:
runtime/mercury_wrapper.c:
Conform to these changes.
---
runtime/Mmakefile | 4 +-
runtime/mercury_context.c | 2 +-
runtime/mercury_engine.c | 2 +-
runtime/mercury_memory_handlers.c | 2 +-
runtime/mercury_par_builtin.h | 2 +-
runtime/mercury_par_profile.c | 2269 +++++++++++++++++++++++++++++++++++++
runtime/mercury_par_profile.h | 242 ++++
runtime/mercury_thread.c | 4 +-
runtime/mercury_threadscope.c | 2269 -------------------------------------
runtime/mercury_threadscope.h | 242 ----
runtime/mercury_wrapper.c | 4 +-
11 files changed, 2521 insertions(+), 2521 deletions(-)
create mode 100644 runtime/mercury_par_profile.c
create mode 100644 runtime/mercury_par_profile.h
delete mode 100644 runtime/mercury_threadscope.c
delete mode 100644 runtime/mercury_threadscope.h
diff --git a/runtime/Mmakefile b/runtime/Mmakefile
index 77f8e51..4bd8cce 100644
--- a/runtime/Mmakefile
+++ b/runtime/Mmakefile
@@ -74,6 +74,7 @@ HDRS = \
mercury_mm_own_stacks.h \
mercury_overflow.h \
mercury_par_builtin.h \
+ mercury_par_profile.h \
mercury_proc_id.h \
mercury_prof.h \
mercury_prof_mem.h \
@@ -95,7 +96,6 @@ HDRS = \
mercury_tags.h \
mercury_term_size.h \
mercury_thread.h \
- mercury_threadscope.h \
mercury_timing.h \
mercury_trace_base.h \
mercury_trace_term.h \
@@ -189,6 +189,7 @@ CFILES = \
mercury_mm_own_stacks.c \
mercury_overflow.c \
mercury_par_builtin.c \
+ mercury_par_profile.c \
mercury_prof.c \
mercury_profiling_builtin.c \
mercury_prof_mem.c \
@@ -206,7 +207,6 @@ CFILES = \
mercury_tabling.c \
mercury_term_size.c \
mercury_thread.c \
- mercury_threadscope.c \
mercury_timing.c \
mercury_trace_base.c \
mercury_trace_term.c \
diff --git a/runtime/mercury_context.c b/runtime/mercury_context.c
index db79ee3..37200a9 100644
--- a/runtime/mercury_context.c
+++ b/runtime/mercury_context.c
@@ -53,7 +53,7 @@ ENDINIT
#include "mercury_memory_handlers.h"
#include "mercury_context.h"
#include "mercury_engine.h" /* for `MR_memdebug' */
-#include "mercury_threadscope.h" /* for data types and posting events */
+#include "mercury_par_profile.h" /* for data types and posting events */
#include "mercury_reg_workarounds.h" /* for `MR_fd*' stuff */
#ifdef MR_PROFILE_PARALLEL_EXECUTION_SUPPORT
diff --git a/runtime/mercury_engine.c b/runtime/mercury_engine.c
index 3aca652..d7c0705 100644
--- a/runtime/mercury_engine.c
+++ b/runtime/mercury_engine.c
@@ -20,7 +20,7 @@ ENDINIT
#include "mercury_engine.h"
#include "mercury_memory_zones.h" /* for MR_create_zone() */
#include "mercury_memory_handlers.h" /* for MR_default_handler() */
-#include "mercury_threadscope.h" /* for event posting */
+#include "mercury_par_profile.h" /* for event posting */
#include "mercury_dummy.h"
diff --git a/runtime/mercury_memory_handlers.c b/runtime/mercury_memory_handlers.c
index 6d455ce..86374d0 100644
--- a/runtime/mercury_memory_handlers.c
+++ b/runtime/mercury_memory_handlers.c
@@ -56,7 +56,7 @@
#include "mercury_memory_zones.h"
#include "mercury_memory_handlers.h"
#include "mercury_faultaddr.h"
-#include "mercury_threadscope.h"
+#include "mercury_par_profile.h"
/*---------------------------------------------------------------------------*/
diff --git a/runtime/mercury_par_builtin.h b/runtime/mercury_par_builtin.h
index a6b86bb..013ee19 100644
--- a/runtime/mercury_par_builtin.h
+++ b/runtime/mercury_par_builtin.h
@@ -20,7 +20,7 @@ vim: ft=c ts=4 sw=4 et
#include "mercury_context.h"
#include "mercury_thread.h"
-#include "mercury_threadscope.h"
+#include "mercury_par_profile.h"
#include "mercury_atomic_ops.h"
/***************************************************************************
diff --git a/runtime/mercury_par_profile.c b/runtime/mercury_par_profile.c
new file mode 100644
index 0000000..789e6b0
--- /dev/null
+++ b/runtime/mercury_par_profile.c
@@ -0,0 +1,2269 @@
+/*
+** vim: ts=4 sw=4 expandtab
+*/
+/*
+** Copyright (C) 2009-2011,2013 The University of Melbourne.
+** Copyright (C) 2008-2009 The GHC Team.
+**
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING.LIB in the Mercury distribution.
+*/
+
+/*
+** Event log format
+**
+** The log format is designed to be extensible: old tools should be able
+** to parse (but not necessarily understand) new versions of the format,
+** and new tools will be able to understand old log files.
+**
+** Each event has a specific format. If you add new events, give them
+** new numbers: we never re-use old event numbers.
+**
+** - The format is endian-independent: all values are represented in
+** bigendian order.
+**
+** - The format is extensible:
+**
+** - The header describes each event type and its length. Tools that
+** don't recognise a particular event type can skip those events.
+**
+** - There is room for extra information in the event type
+** specification, which can be ignored by older tools.
+**
+** - Events can have extra information added, but existing fields
+** cannot be changed. Tools should ignore extra fields at the
+** end of the event record.
+**
+** - Old event type ids are never re-used; just take a new identifier.
+**
+**
+** The format
+** ----------
+**
+** log : EVENT_HEADER_BEGIN
+** EventType*
+** EVENT_HEADER_END
+** EVENT_DATA_BEGIN
+** Event*
+** EVENT_DATA_END
+**
+** EventType :
+** EVENT_ET_BEGIN
+** Word16 -- unique identifier for this event
+** Int16 -- >=0 size of the event in bytes (minus the header)
+** -- -1 variable size
+** Word32 -- length of the next field in bytes
+** Word8* -- string describing the event
+** Word32 -- length of the next field in bytes
+** EventTypeExt* -- extensions
+** EVENT_ET_END
+**
+** Event :
+** Word16 -- event_type
+** Word64 -- time (nanosecs)
+** [Word16] -- length of the rest (for variable-sized events only)
+** ... extra event-specific info ...
+**
+** EventTypeExt :
+** Word16 -- unique identifier for this extension type.
+** Word16 -- size of the payload in bytes.
+** Word8 -- payload bytes, their meaning depends upon the type.
+**
+** EVENT_EXT_TYPE_EXTENSION
+** This event extends another event also defined in this file, the payload of
+** this extension is:
+** Word16 -- unique identifier of the event being extended
+**
+** All values are packed, no attempt is made to align them.
+**
+** New events must be registered with GHC. These are kept in the GHC-events
+** package.
+**
+*/
+
+#include "mercury_imp.h"
+#include "mercury_par_profile.h"
+#include "mercury_atomic_ops.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef MR_THREADSCOPE
+
+/***************************************************************************/
+
+/*
+** Markers for begin/end of the Header.
+*/
+#define MR_TS_EVENT_HEADER_BEGIN 0x68647262 /* 'h' 'd' 'r' 'b' */
+#define MR_TS_EVENT_HEADER_END 0x68647265 /* 'h' 'd' 'r' 'e' */
+
+#define MR_TS_EVENT_DATA_BEGIN 0x64617462 /* 'd' 'a' 't' 'b' */
+#define MR_TS_EVENT_DATA_END 0xffff
+
+/*
+** Markers for begin/end of the list of Event Types in the Header.
+** Header, Event Type, Begin = hetb
+** Header, Event Type, End = hete
+*/
+#define MR_TS_EVENT_HET_BEGIN 0x68657462 /* 'h' 'e' 't' 'b' */
+#define MR_TS_EVENT_HET_END 0x68657465 /* 'h' 'e' 't' 'e' */
+
+/*
+** Markers for the beginning and end of individual event types.
+*/
+#define MR_TS_EVENT_ET_BEGIN 0x65746200 /* 'e' 't' 'b' 0 */
+#define MR_TS_EVENT_ET_END 0x65746500 /* 'e' 't' 'e' 0 */
+
+/*
+** The threadscope events:
+*/
+#define MR_TS_EVENT_CREATE_THREAD 0 /* (thread) */
+#define MR_TS_EVENT_RUN_THREAD 1 /* (thread) */
+#define MR_TS_EVENT_STOP_THREAD 2 /* (thread, status) */
+#define MR_TS_EVENT_THREAD_RUNNABLE 3 /* (thread) */
+#define MR_TS_EVENT_MIGRATE_THREAD 4 /* (thread, new_cap) */
+#define MR_TS_EVENT_SHUTDOWN 7 /* () */
+#define MR_TS_EVENT_THREAD_WAKEUP 8 /* (thread, other_cap) */
+#define MR_TS_EVENT_GC_START 9 /* () */
+#define MR_TS_EVENT_GC_END 10 /* () */
+#define MR_TS_EVENT_REQUEST_SEQ_GC 11 /* () */
+#define MR_TS_EVENT_REQUEST_PAR_GC 12 /* () */
+#define MR_TS_EVENT_CREATE_SPARK_THREAD 15 /* (spark_thread) */
+#define MR_TS_EVENT_LOG_MSG 16 /* (message ...) */
+#define MR_TS_EVENT_STARTUP 17 /* (num_capabilities) */
+#define MR_TS_EVENT_BLOCK_MARKER 18 /* (size, end_time, capability) */
+#define MR_TS_EVENT_USER_MSG 19 /* (message ...) */
+#define MR_TS_EVENT_GC_IDLE 20 /* () */
+#define MR_TS_EVENT_GC_WORK 21 /* () */
+#define MR_TS_EVENT_GC_DONE 22 /* () */
+
+/* 23, 24 used by eden */
+
+/*
+** Capsets or capability sets are groups of engines with some association,
+** for instance a group of threads in a process.
+*/
+#define MR_TS_EVENT_CAPSET_CREATE 25 /* (capset, capset_type) */
+#define MR_TS_EVENT_CAPSET_DELETE 26 /* (capset) */
+#define MR_TS_EVENT_CAPSET_ASSIGN_CAP 27 /* (capset, cap) */
+#define MR_TS_EVENT_CAPSET_REMOVE_CAP 28 /* (capset, cap) */
+/* the RTS identifier is in the form of "GHC-version rts_way" */
+#define MR_TS_EVENT_RTS_IDENTIFIER 29 /* (capset, name_version_string) */
+/* the vectors in two these events are null separated strings */
+#define MR_TS_EVENT_PROGRAM_ARGS 30 /* (capset, commandline_vector) */
+#define MR_TS_EVENT_PROGRAM_ENV 31 /* (capset, environment_vector) */
+
+#define MR_TS_EVENT_OSPROCESS_PID 32 /* (capset, pid) */
+#define MR_TS_EVENT_OSPROCESS_PPID 33 /* (capset, parent_pid) */
+#define MR_TS_EVENT_SPARK_COUNTERS 34 /* (crt,dud,ovf,cnv,fiz,gcd,rem) */
+#define MR_TS_EVENT_SPARK_CREATE 35 /* () */
+#define MR_TS_EVENT_SPARK_DUD 36 /* () */
+#define MR_TS_EVENT_SPARK_OVERFLOW 37 /* () */
+#define MR_TS_EVENT_SPARK_RUN 38 /* () */
+#define MR_TS_EVENT_SPARK_STEAL 39 /* (victim_cap) */
+#define MR_TS_EVENT_SPARK_FIZZLE 40 /* () */
+#define MR_TS_EVENT_SPARK_GC 41 /* () */
+#define MR_TS_EVENT_INTERN_STRING 42 /* (string, id) */
+#define MR_TS_EVENT_WALL_CLOCK_TIME 43 /* (capset, unix_epoch_seconds, nanoseconds) */
+#define MR_TS_EVENT_THREAD_LABEL 44 /* (thread, name_string) */
+#define MR_TS_EVENT_CAP_CREATE 45 /* (cap) */
+#define MR_TS_EVENT_CAP_DELETE 46 /* (cap) */
+#define MR_TS_EVENT_CAP_DISABLE 47 /* (cap) */
+#define MR_TS_EVENT_CAP_ENABLE 48 /* (cap) */
+#define MR_TS_EVENT_HEAP_ALLOCATED 49 /* (heap_capset, alloc_bytes) */
+#define MR_TS_EVENT_HEAP_SIZE 50 /* (heap_capset, size_bytes) */
+#define MR_TS_EVENT_HEAP_LIVE 51 /* (heap_capset, live_bytes) */
+#define MR_TS_EVENT_HEAP_INFO_GHC 52 /* (heap_capset, n_generations,
+ max_heap_size, alloc_area_size,
+ mblock_size, block_size) */
+#define MR_TS_EVENT_GC_STATS_GHC 53 /* (heap_capset, generation,
+ copied_bytes, slop_bytes, frag_bytes,
+ par_n_threads,
+ par_max_copied, par_tot_copied) */
+#define MR_TS_EVENT_GC_GLOBAL_SYNC 54 /* () */
+
+#define MR_TS_NUM_EVENT_TAGS 55
+
+#define MR_TS_MER_EVENT_START 100
+
+#define MR_TS_MER_EVENT_START_PAR_CONJ 100 /* (int id, memo'd string id) */
+#define MR_TS_MER_EVENT_END_PAR_CONJ 101 /* (int id) */
+#define MR_TS_MER_EVENT_END_PAR_CONJUNCT 102 /* (int id) */
+
+/*
+** Creating sparks is not specifically mercury, but conjunct IDs are.
+** If other systems wish to use this event, they can move it
+** to the main events section.
+*/
+#define MR_TS_MER_EVENT_SPARK_CREATE 103 /* (int id, spark id) */
+
+#define MR_TS_MER_EVENT_FUT_CREATE 104 /* (fut id, memo'd name id) */
+#define MR_TS_MER_EVENT_FUT_WAIT_NOSUSPEND 105 /* (fut id) */
+#define MR_TS_MER_EVENT_FUT_WAIT_SUSPENDED 106 /* (fut id) */
+#define MR_TS_MER_EVENT_FUT_SIGNAL 107 /* (fut id) */
+#define MR_TS_MER_EVENT_LOOKING_FOR_GLOBAL_CONTEXT \
+ 108 /* () */
+#define MR_TS_MER_EVENT_WORK_STEALING 109 /* () */
+#define MR_TS_MER_EVENT_RELEASE_CONTEXT 110 /* (context id) */
+#define MR_TS_MER_EVENT_ENGINE_SLEEPING 111 /* () */
+#define MR_TS_MER_EVENT_LOOKING_FOR_LOCAL_SPARK \
+ 112 /* () */
+#define MR_TS_MER_EVENT_CALLING_MAIN 113 /* () */
+#define MR_TS_MER_EVENT_SPARK_RUN 114 /* (spark id) */
+#define MR_TS_MER_EVENT_SPARK_STEAL 115 /* (victim cap, spark id) */
+#define MR_TS_MER_EVENT_REUSE_THREAD 116 /* (context id, old context id) */
+#define MR_TS_NUM_MER_EVENTS 17
+
+#if 0 /* DEPRECATED EVENTS: */
+#define EVENT_CREATE_SPARK 13 /* (cap, thread) */
+#define EVENT_SPARK_TO_THREAD 14 /* (cap, thread, spark_thread) */
+#define MR_TS_EVENT_RUN_SPARK 5 /* (thread, spark_id) */
+#define MR_TS_EVENT_STEAL_SPARK 6 /* (thread, victim_cap, spark_id) */
+#endif
+
+/*
+** Engine set type values for EVENT_CAPSET_CREATE.
+*/
+#define MR_TS_ENGSET_TYPE_CUSTOM 1 /* reserved for end-user applications */
+#define MR_TS_ENGSET_TYPE_OSPROCESS 2 /* engines belong to same OS process */
+#define MR_TS_ENGSET_TYPE_CLOCKDOMAIN 3 /* engines share a local clock/time */
+
+/*
+** Event extension types
+*/
+#define MR_EXT_TYPE_EXTENSION 1 /* This event extends another event */
+
+/*
+** GHC uses 2MB per buffer. Note that the minimum buffer size is the size of
+** the largest message plus the size of the block marker message, however it is
+** _sensible_ for the buffer to be much larger so that we make system calls
+** less often.
+*/
+#define MR_TS_BUFFERSIZE (2*1024*1024)
+#define MR_TS_FILENAME_FORMAT ("%s.eventlog")
+#define MR_TSC_SYNC_NUM_ROUNDS (10)
+#define MR_TSC_SYNC_NUM_BEST_ROUNDS (3)
+
+/* Uncomment this to enable some debugging code */
+/* #define MR_DEBUG_THREADSCOPE 1 */
+
+#if MR_DEBUG_THREADSCOPE
+#define MR_DO_THREADSCOPE_DEBUG(x) do { x; } while(0)
+#else
+#define MR_DO_THREADSCOPE_DEBUG(x)
+#endif
+
+/***************************************************************************/
+
+struct MR_threadscope_event_buffer {
+ unsigned char MR_tsbuffer_data[MR_TS_BUFFERSIZE];
+
+ /* The current writing position in the buffer. */
+ MR_Unsigned MR_tsbuffer_pos;
+
+ /* The position of the start of the most recent block. */
+ MR_Integer MR_tsbuffer_block_open_pos;
+
+ /*
+ ** True if the engine's current context is stopped, and therefore
+ ** stop and start events should not be posted from the GC callback
+ ** procedures.
+ */
+ MR_bool MR_tsbuffer_ctxt_is_stopped;
+
+ /* A cheap userspace lock to make buffers reentrant. */
+ volatile MR_Us_Lock MR_tsbuffer_lock;
+};
+
+/*
+** We define some types and functions to write them.
+** These types are set carefully to match the ones that GHC uses.
+*/
+typedef MR_uint_least16_t EventType;
+typedef MR_uint_least64_t Time;
+typedef MR_int_least64_t Timedelta;
+
+/*
+** The difference between two positions in the eventlog file measured in bytes.
+*/
+typedef MR_uint_least32_t EventlogOffset;
+
+/*
+** A descriptor used when writing the header of threadscope files.
+** The fields are:
+**
+** edt_event_type The type of this event
+**
+** edt_description A string description of this event
+**
+** edt_size The event's size or -1 for variable length
+**
+** edt_extends_event The event that this event extends, or 0xFFFF if this is
+** a base event
+*/
+typedef struct {
+ EventType etd_event_type;
+ const char *etd_description;
+ MR_int_least16_t etd_size;
+ EventType edt_extends_event;
+} EventTypeDesc;
+
+/***************************************************************************/
+
+#define SZ_EVENT_TYPE 2
+#define SZ_CAPSET_ID 4
+#define SZ_CAPSET_TYPE 2
+#define SZ_CONTEXT_ID 4
+#define SZ_CONTEXT_STOP_REASON 2
+#define SZ_DYN_CONJ_ID 8
+#define SZ_ENGINELOG_OFFSET 4
+#define SZ_ENGINE_ID 2
+#define SZ_PID 4
+#define SZ_SPARK_ID 4
+#define SZ_STRING_ID 4
+#define SZ_STATIC_CONJ_ID (SZ_STRING_ID)
+#define SZ_VAR_NAME_ID (SZ_STRING_ID)
+#define SZ_TIME 8
+#define SZ_FUTURE_ID 8
+
+static EventTypeDesc event_type_descs[] = {
+ {
+ /*
+ ** The startup event informs threadscope of the number of engines
+ ** we are using. It should be given outside of a block.
+ */
+ MR_TS_EVENT_STARTUP,
+ "Startup (num_engines)",
+ SZ_ENGINE_ID,
+ 0xFFFF
+ },
+ {
+ /*
+ ** The last event in the log. It should be given outside of a block.
+ */
+ MR_TS_EVENT_SHUTDOWN,
+ "Shutdown",
+ 0,
+ 0xFFFF
+ },
+ {
+ /*
+ ** A block of events belonging to the named engine follows.
+ ** The length of this block is given including the block message
+ ** itself, the time that this block finishes is also given.
+ ** Blocks _must not_ exist within other blocks.
+ */
+ MR_TS_EVENT_BLOCK_MARKER,
+ "A block of events generated by a specific engine follows",
+ SZ_ENGINELOG_OFFSET + SZ_TIME + SZ_ENGINE_ID,
+ 0xFFFF
+ },
+ {
+ /*
+ ** Called when a context is created or re-used.
+ */
+ MR_TS_EVENT_CREATE_THREAD,
+ "A context is created or re-used",
+ SZ_CONTEXT_ID,
+ 0xFFFF
+ },
+ {
+ /*
+ ** Called from MR_schedule_context()
+ */
+ MR_TS_EVENT_THREAD_RUNNABLE,
+ "The context is being placed on the run queue",
+ SZ_CONTEXT_ID,
+ 0xFFFF
+ },
+ {
+ /*
+ ** The engine has taken a spark from it's local stack and will run it.
+ */
+ MR_TS_EVENT_SPARK_RUN,
+ "Run a spark from the local stack",
+ 0,
+ 0xFFFF
+ },
+ {
+ MR_TS_MER_EVENT_SPARK_RUN,
+ "Run a spark from the local stack, the spark is identified by an id",
+ SZ_SPARK_ID,
+ MR_TS_EVENT_SPARK_RUN
+ },
+ {
+ /*
+ ** The named context has begun executing a spark from another
+ ** engine's stack.
+ */
+ MR_TS_EVENT_SPARK_STEAL,
+ "Run a spark stolen from another engine",
+ SZ_ENGINE_ID,
+ 0XFFFF
+ },
+ {
+ MR_TS_MER_EVENT_SPARK_STEAL,
+ "Run a spark stolen from another engine, "
+ "the spark is identified by an id",
+ SZ_ENGINE_ID + SZ_SPARK_ID,
+ MR_TS_EVENT_SPARK_STEAL
+ },
+ {
+ /*
+ ** The named context has begun executing on the engine
+ ** named by the current block.
+ */
+ MR_TS_EVENT_RUN_THREAD,
+ "Run context",
+ SZ_CONTEXT_ID,
+ 0xFFFF
+ },
+ {
+ /*
+ ** The named context has stopped executing on the engine named by
+ ** the current block. The reason why the context stopped is given.
+ */
+ MR_TS_EVENT_STOP_THREAD,
+ "Context stopped",
+ SZ_CONTEXT_ID + SZ_CONTEXT_STOP_REASON,
+ 0xFFFF
+ },
+ {
+ /*
+ ** This event is posted when a context is created for a spark.
+ */
+ MR_TS_EVENT_CREATE_SPARK_THREAD,
+ "Create a context for executing a spark",
+ SZ_CONTEXT_ID,
+ 0xFFFF
+ },
+ {
+ MR_TS_EVENT_LOG_MSG,
+ "A user-provided log message",
+ -1, /* Variable length */
+ 0xFFFF
+ },
+ {
+ /*
+ ** Start a garbage collection run.
+ */
+ MR_TS_EVENT_GC_START,
+ "Start GC",
+ 0,
+ 0xFFFF
+ },
+ {
+ /*
+ ** Stop a garbage collection run.
+ */
+ MR_TS_EVENT_GC_END,
+ "Stop GC",
+ 0,
+ 0xFFFF
+ },
+ {
+ /*
+ ** The runtime system registers a string and an ID for it
+ ** so that the ID represents the string in future messages.
+ */
+ MR_TS_EVENT_INTERN_STRING,
+ "Register an id->string mapping",
+ -1,
+ 0xFFFF
+ },
+ {
+ MR_TS_EVENT_CAPSET_CREATE,
+ "Create an engine set",
+ SZ_CAPSET_ID + SZ_CAPSET_TYPE,
+ 0xFFFF
+ },
+ {
+ MR_TS_EVENT_CAPSET_DELETE,
+ "Detete an engine set",
+ SZ_CAPSET_ID,
+ 0xFFFF
+ },
+ {
+ MR_TS_EVENT_CAPSET_ASSIGN_CAP,
+ "Add an engine to an engine set",
+ SZ_CAPSET_ID + SZ_ENGINE_ID,
+ 0xFFFF
+ },
+ {
+ MR_TS_EVENT_CAPSET_REMOVE_CAP,
+ "Add an engine to an engine set",
+ SZ_CAPSET_ID + SZ_ENGINE_ID,
+ 0xFFFF
+ },
+ {
+ MR_TS_EVENT_RTS_IDENTIFIER,
+ "The type of the runtime system for this capset",
+ -1,
+ 0xFFFF
+ },
+ {
+ MR_TS_EVENT_PROGRAM_ARGS,
+ "The command line arguments of this process",
+ -1,
+ 0xFFFF
+ },
+ {
+ MR_TS_EVENT_PROGRAM_ENV,
+ "The environment variables this process inherited",
+ -1,
+ 0xFFFF
+ },
+ {
+ MR_TS_EVENT_OSPROCESS_PID,
+ "The pid of this process",
+ SZ_PID,
+ 0xFFFF
+ },
+ {
+ MR_TS_EVENT_OSPROCESS_PPID,
+ "The parent pid of this process",
+ SZ_PID,
+ 0xFFFF
+ },
+ {
+ MR_TS_EVENT_SPARK_CREATE,
+ "A spark is being created",
+ 0,
+ 0xFFFF
+ },
+ /*
+ * We don't use events 43--53.
+ */
+ {
+ MR_TS_EVENT_GC_GLOBAL_SYNC,
+ /*
+ ** If using parallel marking this also means that marker threads are
+ ** ready. This doesn't apply to Mercury as Boehm uses seperate
+ ** threads
+ */
+ "The world has stopped and GC may begin",
+ 0,
+ 0xFFFF
+ },
+ {
+ MR_TS_MER_EVENT_SPARK_CREATE,
+ "A spark is being created with attributes for Mercury",
+ SZ_DYN_CONJ_ID + SZ_SPARK_ID,
+ MR_TS_EVENT_SPARK_CREATE
+ },
+ {
+ MR_TS_MER_EVENT_START_PAR_CONJ,
+ "Start a parallel conjunction (dyn id, static id)",
+ SZ_DYN_CONJ_ID + SZ_STATIC_CONJ_ID,
+ 0xFFFF
+ },
+ {
+ MR_TS_MER_EVENT_END_PAR_CONJ,
+ "End a parallel conjunction (dyn id)",
+ SZ_DYN_CONJ_ID,
+ 0xFFFF
+ },
+ {
+ MR_TS_MER_EVENT_END_PAR_CONJUNCT,
+ "End a parallel conjunct (dyn id)",
+ SZ_DYN_CONJ_ID,
+ 0xFFFF
+ },
+ {
+ /*
+ ** The dynamic conjunction id can be inferred from context;
+ ** it is the next conjunction started after this event.
+ */
+ MR_TS_MER_EVENT_FUT_CREATE,
+ "Create a future (future id)",
+ SZ_FUTURE_ID + SZ_VAR_NAME_ID,
+ 0xFFFF
+ },
+ {
+ MR_TS_MER_EVENT_FUT_WAIT_NOSUSPEND,
+ "Wait on a future without suspending (future id)",
+ SZ_FUTURE_ID,
+ 0xFFFF
+ },
+ {
+ MR_TS_MER_EVENT_FUT_WAIT_SUSPENDED,
+ "Wait on a future by suspending this thread (future id)",
+ SZ_FUTURE_ID,
+ 0xFFFF
+ },
+ {
+ MR_TS_MER_EVENT_FUT_SIGNAL,
+ "Signal a future (future id)",
+ SZ_FUTURE_ID,
+ 0xFFFF
+ },
+ {
+ MR_TS_MER_EVENT_LOOKING_FOR_GLOBAL_CONTEXT,
+ "Engine begins looking for a context to execute",
+ 0,
+ 0xFFFF
+ },
+ {
+ MR_TS_MER_EVENT_LOOKING_FOR_LOCAL_SPARK,
+ "Engine begins looking for a local spark to execute",
+ 0,
+ 0xFFFF
+ },
+ {
+ MR_TS_MER_EVENT_WORK_STEALING,
+ "Engine begins attempt to steal work",
+ 0,
+ 0xFFFF
+ },
+ {
+ MR_TS_MER_EVENT_RELEASE_CONTEXT,
+ "Release this context to the free context pool",
+ SZ_CONTEXT_ID,
+ 0xFFFF
+ },
+ {
+ MR_TS_MER_EVENT_ENGINE_SLEEPING,
+ "This engine is going to sleep",
+ 0,
+ 0xFFFF
+ },
+ {
+ /*
+ ** The runtime system is about to call main/2.
+ ** This message has no parameters.
+ */
+ MR_TS_MER_EVENT_CALLING_MAIN,
+ "About to call main/2",
+ 0,
+ 0xFFFF
+ },
+ {
+ /*
+ ** The runtime system is re-useing a previous context and
+ ** re-assigning its ID.
+ */
+ MR_TS_MER_EVENT_REUSE_THREAD,
+ "Reusing a previously allocated thread",
+ SZ_CONTEXT_ID + SZ_CONTEXT_ID,
+ MR_TS_EVENT_CREATE_THREAD
+ },
+ {
+ /* Mark the end of this array. */
+ MR_TS_NUM_EVENT_TAGS,
+ NULL,
+ 0,
+ 0xFFFF
+ }
+};
+
+/*
+** These tables are filled in when the header of the log file is written.
+** While they can be inferred from the event_type_desc structure,
+** they allow for constant time lookup.
+*/
+static MR_int_least16_t event_type_sizes[MR_TS_NUM_EVENT_TAGS];
+static MR_int_least16_t event_type_sizes_mercury[MR_TS_NUM_MER_EVENTS];
+
+static FILE* MR_threadscope_output_file = NULL;
+static char* MR_threadscope_output_filename;
+
+/*
+** The TSC value recorded when the primordial thread called
+** MR_setup_threadscope(), this is used retroactivly to initialise the
+** MR_eng_cpu_clock_ticks_offset field in the engine structure once it is
+** created.
+*/
+static MR_uint_least64_t MR_primordial_first_tsc;
+
+static Timedelta MR_global_offset;
+
+static struct MR_threadscope_event_buffer global_buffer;
+
+/*
+** Alternativly we use gettimeofday for measuring time.
+*/
+MR_bool MR_threadscope_use_tsc = MR_FALSE;
+static Timedelta MR_gettimeofday_offset;
+
+/*
+** An ID that may be allocated to the next string to be registered.
+*/
+static MR_TS_StringId MR_next_string_id = 0;
+static MR_EngSetId next_engset_id = 0;
+
+static MR_EngSetId process_engset_id;
+
+/***************************************************************************/
+
+static MR_EngSetId
+get_next_engset_id(void)
+{
+ /*
+ ** This is a seperate function as I may have to add locking or atomic ops
+ ** later.
+ */
+ return next_engset_id++;
+}
+
+/***************************************************************************/
+
+MR_STATIC_INLINE MR_int_least16_t
+event_type_size(EventType event_type) {
+ MR_int_least16_t size;
+
+ if (event_type < MR_TS_NUM_EVENT_TAGS) {
+ size = event_type_sizes[event_type];
+ } else if ((event_type < (MR_TS_MER_EVENT_START + MR_TS_NUM_MER_EVENTS))
+ && (event_type >= MR_TS_MER_EVENT_START)) {
+ size = event_type_sizes_mercury[event_type - MR_TS_MER_EVENT_START];
+ } else {
+ fprintf(stderr, "Unknown event type %d\n", event_type);
+ abort();
+ }
+
+ return size;
+}
+
+/*
+** Is there enough room in the current engine's buffer
+** for this statically sized event _and_ for the block marker event.
+*/
+MR_STATIC_INLINE MR_bool
+enough_room_for_event(struct MR_threadscope_event_buffer *buffer,
+ EventType event_type)
+{
+ int needed =
+ buffer->MR_tsbuffer_pos +
+ event_type_size(event_type) +
+ event_type_size(MR_TS_EVENT_BLOCK_MARKER) +
+ ((2 + 8) * 2); /* (EventType, Time) * 2 */
+ return needed < MR_TS_BUFFERSIZE;
+}
+
+MR_STATIC_INLINE MR_bool
+enough_room_for_variable_size_event(struct MR_threadscope_event_buffer *buffer,
+ MR_Unsigned length)
+{
+ int needed =
+ buffer->MR_tsbuffer_pos +
+ length +
+ event_type_size(MR_TS_EVENT_BLOCK_MARKER) +
+ ((2 + 8) * 2); /* (EventType, Time) * 2 */
+ return needed < MR_TS_BUFFERSIZE;
+}
+
+/*
+** Is a block currently open?
+*/
+MR_STATIC_INLINE MR_bool block_is_open(
+ struct MR_threadscope_event_buffer *buffer)
+{
+ return !(buffer->MR_tsbuffer_block_open_pos == -1);
+}
+
+/*
+** Put words into the current engine's buffer in big endian order.
+*/
+MR_STATIC_INLINE void put_byte(struct MR_threadscope_event_buffer *buffer,
+ int byte)
+{
+ buffer->MR_tsbuffer_data[buffer->MR_tsbuffer_pos++] = byte;
+}
+
+MR_STATIC_INLINE void put_be_int16(struct MR_threadscope_event_buffer *buffer,
+ MR_int_least16_t word)
+{
+ put_byte(buffer, (word >> 8) & 0xFF);
+ put_byte(buffer, word & 0xFF);
+}
+
+MR_STATIC_INLINE void put_be_uint16(struct MR_threadscope_event_buffer *buffer,
+ MR_uint_least16_t word)
+{
+ put_byte(buffer, (word >> 8) & 0xFF);
+ put_byte(buffer, word & 0xFF);
+}
+
+MR_STATIC_INLINE void put_be_uint32(struct MR_threadscope_event_buffer *buffer,
+ MR_uint_least32_t word)
+{
+ put_be_uint16(buffer, (word >> 16) & 0xFFFF);
+ put_be_uint16(buffer, word & 0xFFFF);
+}
+
+MR_STATIC_INLINE void put_be_uint64(struct MR_threadscope_event_buffer *buffer,
+ MR_uint_least64_t word)
+{
+ put_be_uint32(buffer, (word >> 32) & 0xFFFFFFFF);
+ put_be_uint32(buffer, word & 0xFFFFFFFF);
+}
+
+MR_STATIC_INLINE void put_raw_string(
+ struct MR_threadscope_event_buffer *buffer,
+ const char *string, unsigned len)
+{
+ unsigned i;
+ for (i = 0; i < len; i++) {
+ put_byte(buffer, string[i]);
+ }
+}
+
+/*
+** Put a string in the given buffer. The string will be preceeded
+** by a 16 bit integer giving the string's length.
+*/
+MR_STATIC_INLINE void put_string_size16(
+ struct MR_threadscope_event_buffer *buffer, const char *string)
+{
+ unsigned i, len;
+
+ len = strlen(string);
+ put_be_uint16(buffer, len);
+ put_raw_string(buffer, string, len);
+}
+
+/*
+** Put a string in the given buffer. The string will be preceeded
+** by a 32 bit integer giving the string's length.
+*/
+MR_STATIC_INLINE void put_string_size32(
+ struct MR_threadscope_event_buffer *buffer, const char *string)
+{
+ unsigned i, len;
+
+ len = strlen(string);
+ put_be_uint32(buffer, len);
+ put_raw_string(buffer, string, len);
+}
+
+MR_STATIC_INLINE void put_timestamp(struct MR_threadscope_event_buffer *buffer,
+ Time timestamp)
+{
+ put_be_uint64(buffer, timestamp);
+}
+
+MR_STATIC_INLINE void put_eventlog_offset(
+ struct MR_threadscope_event_buffer *buffer, EventlogOffset offset)
+{
+ put_be_uint32(buffer, offset);
+}
+
+MR_STATIC_INLINE void put_event_header(
+ struct MR_threadscope_event_buffer *buffer,
+ EventType event_type, Time timestamp)
+{
+ put_be_uint16(buffer, event_type);
+ put_timestamp(buffer, timestamp);
+}
+
+MR_STATIC_INLINE void put_engine_id(struct MR_threadscope_event_buffer *buffer,
+ MR_EngineId engine_num)
+{
+ put_be_uint16(buffer, engine_num);
+}
+
+MR_STATIC_INLINE void put_context_id(
+ struct MR_threadscope_event_buffer *buffer, MR_ContextId context_id)
+{
+ put_be_uint32(buffer, context_id);
+}
+
+MR_STATIC_INLINE void put_stop_reason(
+ struct MR_threadscope_event_buffer *buffer, MR_ContextStopReason reason)
+{
+ put_be_uint16(buffer, reason);
+}
+
+MR_STATIC_INLINE void put_string_id(struct MR_threadscope_event_buffer *buffer,
+ MR_TS_StringId id)
+{
+ put_be_uint32(buffer, id);
+}
+
+MR_STATIC_INLINE void put_par_conj_dynamic_id(
+ struct MR_threadscope_event_buffer *buffer, MR_Word* id)
+{
+ put_be_uint64(buffer, (MR_Word)id);
+}
+
+MR_STATIC_INLINE void put_spark_id(struct MR_threadscope_event_buffer *buffer,
+ MR_SparkId spark_id)
+{
+ put_be_uint32(buffer, spark_id);
+}
+
+MR_STATIC_INLINE void put_engset_id(struct MR_threadscope_event_buffer *buffer,
+ MR_EngSetId engset_id)
+{
+ put_be_uint32(buffer, engset_id);
+}
+
+MR_STATIC_INLINE void put_engset_type(
+ struct MR_threadscope_event_buffer *buffer, MR_EngSetType type)
+{
+ put_be_uint16(buffer, type);
+}
+
+MR_STATIC_INLINE void put_future_id(struct MR_threadscope_event_buffer *buffer,
+ MR_Future* id)
+{
+ put_be_uint64(buffer, (MR_Word)id);
+}
+
+/***************************************************************************/
+
+static struct MR_threadscope_event_buffer* MR_create_event_buffer(void);
+
+/*
+** The prelude is everything up to and including the 'DATA_BEGIN' marker.
+*/
+static void MR_open_output_file_and_write_prelude(void);
+
+static void MR_close_output_file(void);
+
+static void put_event_type(struct MR_threadscope_event_buffer *buffer,
+ EventTypeDesc *event_type);
+
+static MR_bool flush_event_buffer(struct MR_threadscope_event_buffer *buffer);
+
+static void maybe_close_block(struct MR_threadscope_event_buffer *buffer);
+
+static void open_block(struct MR_threadscope_event_buffer *buffer,
+ MR_Unsigned eng_id);
+
+/***************************************************************************/
+
+static MR_TS_StringId
+MR_threadscope_register_string(const char *string);
+
+/*
+** These four events are used to create and manage engine sets.
+** Haskell calls these cap sets.
+**
+** The first two work on the global event buffer and are not thread safe.
+*/
+static void MR_threadscope_post_create_engset(MR_EngSetId id,
+ MR_EngSetType type);
+
+static void MR_threadscope_post_destroy_engset(MR_EngSetId id);
+
+static void MR_threadscope_post_engset_add(
+ struct MR_threadscope_event_buffer *buffer,
+ MR_EngSetId id, MR_EngineId eng);
+
+static void MR_threadscope_post_engset_remove(MR_EngSetId id, MR_EngineId eng);
+
+/*
+** Post the name and version of the runtime system to the log file.
+**
+** Note that this is the name of the implementation (mmc),
+** not the name of the language (mercury).
+**
+** The name and version are separated by a '-'.
+*/
+static void MR_threadscope_post_runtime_identifier(MR_EngSetId id,
+ const char *ident);
+
+/***************************************************************************/
+
+static void start_gc_callback(void);
+static void stop_gc_callback(void);
+static void pause_thread_gc_callback(void);
+static void resume_thread_gc_callback(void);
+
+/***************************************************************************/
+
+static Time get_current_time_nanosecs(void);
+static Time gettimeofday_nsecs(void);
+
+/***************************************************************************/
+
+void
+MR_setup_threadscope(void)
+{
+ MR_DO_THREADSCOPE_DEBUG(
+ fprintf(stderr, "In setup threadscope thread: 0x%lx\n", pthread_self())
+ );
+
+ if (!MR_tsc_is_sensible()) {
+ MR_threadscope_use_tsc = MR_FALSE;
+ }
+
+ if (MR_threadscope_use_tsc) {
+ /* This value is used later when setting up the primordial engine. */
+ MR_primordial_first_tsc = MR_read_cpu_tsc();
+
+ /*
+ ** These variables are used for TSC synchronization which is not used.
+ ** See below.
+ **
+ pthread_mutex_init(&MR_tsc_sync_slave_lock, MR_MUTEX_ATTR);
+ MR_US_COND_CLEAR(&MR_tsc_sync_slave_entry_cond);
+ MR_US_COND_CLEAR(&MR_tsc_sync_master_entry_cond);
+ MR_US_COND_CLEAR(&MR_tsc_sync_t0);
+ MR_US_COND_CLEAR(&MR_tsc_sync_t1);
+ */
+
+ } else {
+ MR_gettimeofday_offset = -1 * gettimeofday_nsecs();
+ }
+
+ /* Configure Boehm */
+#ifdef MR_BOEHM_GC
+ GC_mercury_callback_start_collect = start_gc_callback;
+ GC_mercury_callback_stop_collect = stop_gc_callback;
+ GC_mercury_callback_pause_thread = pause_thread_gc_callback;
+ GC_mercury_callback_resume_thread = resume_thread_gc_callback;
+#endif
+
+ /* Clear the global buffer and setup the file */
+ global_buffer.MR_tsbuffer_pos = 0;
+ global_buffer.MR_tsbuffer_block_open_pos = -1;
+ global_buffer.MR_tsbuffer_lock = MR_US_LOCK_INITIAL_VALUE;
+ MR_open_output_file_and_write_prelude();
+
+ /*
+ ** Post the initial events to the buffer.
+ */
+ process_engset_id = get_next_engset_id();
+ MR_threadscope_post_create_engset(process_engset_id,
+ MR_TS_ENGSET_TYPE_OSPROCESS);
+ MR_threadscope_post_runtime_identifier(process_engset_id,
+ "mmc-" MR_VERSION);
+
+ /*
+ ** Put the startup event in the buffer.
+ */
+ put_event_header(&global_buffer, MR_TS_EVENT_STARTUP, 0);
+ put_engine_id(&global_buffer, (MR_EngineId)MR_num_threads);
+
+ flush_event_buffer(&global_buffer);
+}
+
+void
+MR_finalize_threadscope(void)
+{
+ MR_DO_THREADSCOPE_DEBUG(
+ fprintf(stderr, "In finalize threadscope thread: 0x%lx\n",
+ pthread_self())
+ );
+
+ MR_threadscope_post_destroy_engset(process_engset_id);
+
+ flush_event_buffer(&global_buffer);
+ MR_close_output_file();
+}
+
+void
+MR_threadscope_setup_engine(MercuryEngine *eng)
+{
+ MR_DO_THREADSCOPE_DEBUG(
+ fprintf(stderr, "In threadscope setup engine thread: 0x%lx\n",
+ pthread_self())
+ );
+ eng->MR_eng_next_spark_id = 0;
+
+ if (MR_threadscope_use_tsc) {
+ if (eng->MR_eng_id == 0) {
+ MR_global_offset = -MR_primordial_first_tsc;
+ }
+ eng->MR_eng_cpu_clock_ticks_offset = MR_global_offset;
+ }
+
+ eng->MR_eng_ts_buffer = MR_create_event_buffer();
+
+ MR_threadscope_post_engset_add(eng->MR_eng_ts_buffer, process_engset_id,
+ eng->MR_eng_id);
+ /*
+ ** Flush the buffer to ensure the message above (which lacks a timestamp)
+ ** appears in a sensible place in the buffer.
+ */
+ flush_event_buffer(eng->MR_eng_ts_buffer);
+}
+
+void
+MR_threadscope_finalize_engine(MercuryEngine *eng)
+{
+ struct MR_threadscope_event_buffer *buffer = eng->MR_eng_ts_buffer;
+
+ MR_DO_THREADSCOPE_DEBUG(
+ fprintf(stderr, "In threadscope finalize engine thread: 0x%lx\n",
+ pthread_self())
+ );
+
+ MR_threadscope_post_engset_remove(process_engset_id, eng->MR_eng_id);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+
+ if (!enough_room_for_event(buffer, MR_TS_EVENT_SHUTDOWN)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, eng->MR_eng_id);
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, eng->MR_eng_id);
+ }
+ put_event_header(buffer, MR_TS_EVENT_SHUTDOWN, get_current_time_nanosecs());
+
+ flush_event_buffer(buffer);
+ eng->MR_eng_ts_buffer = NULL;
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+#if 0
+/*
+** It looks like we don't need this on modern CPUs including multi-socket
+** systems (goliath). If we find systems where this is needed, we can
+** enable it via a runtime check.
+*/
+/*
+** The synchronization of TSCs operates as follows:
+** The master and slave enter their functions. Both threads spin until the
+** other is ready (signaling the other before they begin spinning). Then for
+** MR_TSC_SYNC_NUM_ROUNDS: The master spins waiting for the slave. The slave
+** records it's current TSC, signals the master and spins waiting for a reply.
+** The master upon hearing from the slave records it's TSC and then signals
+** the slave. The slave can then compute the delay in this round. The slave
+** takes the NR_TSC_SYNC_NUM_BEST_ROUNDS best delays (smallest) and computes
+** the offset as the average between between the difference of the clocks based
+** on Cristan's algorithm (1989).
+*/
+
+typedef struct {
+ Timedelta delay;
+ Timedelta offset;
+} TimeDelayOffset;
+
+static MercuryLock MR_tsc_sync_slave_lock;
+volatile static MR_Us_Cond MR_tsc_sync_slave_entry_cond;
+volatile static MR_Us_Cond MR_tsc_sync_master_entry_cond;
+volatile static MR_Us_Cond MR_tsc_sync_t0;
+volatile static MR_Us_Cond MR_tsc_sync_t1;
+static Time MR_tsc_sync_master_time;
+
+static int compare_time_delay_offset_by_delay(const void *a, const void *b);
+
+void
+MR_threadscope_sync_tsc_master(void)
+{
+ unsigned i;
+
+ /* Wait for a slave to enter. */
+ MR_US_COND_SET(&MR_tsc_sync_master_entry_cond);
+ MR_US_SPIN_COND(&MR_tsc_sync_slave_entry_cond);
+ MR_US_COND_CLEAR(&MR_tsc_sync_slave_entry_cond);
+
+ for (i = 0; i < MR_TSC_SYNC_NUM_ROUNDS; i++) {
+ /* Wait to receive the message from the slave at T0. */
+ MR_US_SPIN_COND(&MR_tsc_sync_t0);
+ MR_US_COND_CLEAR(&MR_tsc_sync_t0);
+
+ /* Read our TSC and send the slave a message. */
+ MR_tsc_sync_master_time = MR_read_cpu_tsc();
+ MR_US_COND_SET(&MR_tsc_sync_t1);
+ }
+
+}
+
+void
+MR_threadscope_sync_tsc_slave(void)
+{
+ unsigned i, j;
+ TimeDelayOffset delay_offset[MR_TSC_SYNC_NUM_ROUNDS];
+ Timedelta total_offset;
+ MercuryEngine *eng = MR_thread_engine_base;
+
+ /* Only one slave may enter at a time. */
+ MR_LOCK(&MR_tsc_sync_slave_lock, "MR_threadscope_sync_tsc_slave");
+
+ /*
+ ** Tell the master we are ready to begin, and wait for it to tell us
+ ** it is ready.
+ */
+ MR_US_COND_SET(&MR_tsc_sync_slave_entry_cond);
+ MR_US_SPIN_COND(&MR_tsc_sync_master_entry_cond);
+ MR_US_COND_CLEAR(&MR_tsc_sync_master_entry_cond);
+
+ for (i = 0; i < MR_TSC_SYNC_NUM_ROUNDS; i++) {
+ Time slave_tsc_at_t0;
+ Time slave_tsc_at_t2;
+
+ /*
+ ** Get the current time and signal that we've done so (T=0).
+ */
+ slave_tsc_at_t0 = MR_read_cpu_tsc();
+ MR_US_COND_SET(&MR_tsc_sync_t0);
+
+ /*
+ ** Wait for the master to reply, the master handles T=1,
+ ** here we proceed to T=2.
+ */
+ MR_US_SPIN_COND(&MR_tsc_sync_t1);
+ slave_tsc_at_t2 = MR_read_cpu_tsc();
+ MR_US_COND_CLEAR(&MR_tsc_sync_t1);
+
+ /*
+ ** Here are Cristian's formulas. Delay is the round trip time,
+ ** slave_tsc_at_t0 + delay/2 is the time on the slave's clock that the
+ ** master processed the slaves message and sent its own. This is
+ ** accurate if the communication delays in either direction are
+ ** uniform, that is communication latency is synchronous.
+ */
+ delay_offset[i].delay = slave_tsc_at_t2 - slave_tsc_at_t0;
+ delay_offset[i].offset = MR_tsc_sync_master_time -
+ (slave_tsc_at_t0 + delay_offset[i].delay/2);
+ }
+
+ /*
+ ** By now the master thread will return,
+ ** and continue with its normal work.
+ */
+
+ /*
+ ** We do this debugging output while holding the lock, so that the output
+ ** is reasonable.
+ */
+ MR_DO_THREADSCOPE_DEBUG({
+ fprintf(stderr, "TSC Synchronization for thread 0x%x\n",
+ pthread_self());
+ for (i = 0; i < MR_TSC_SYNC_NUM_ROUNDS; i++) {
+ fprintf(stderr,
+ "delay: %ld offset (local + global = total) %ld + %ld = %ld\n",
+ delay_offset[i].delay, delay_offset[i].offset, MR_global_offset,
+ delay_offset[i].offset + MR_global_offset);
+ }
+ });
+ MR_UNLOCK(&MR_tsc_sync_slave_lock, "MR_threadscope_sync_tsc_slave");
+
+ /* Now to average the best offsets. */
+ qsort(&delay_offset, MR_TSC_SYNC_NUM_ROUNDS, sizeof(TimeDelayOffset),
+ compare_time_delay_offset_by_delay);
+ total_offset = 0;
+ for (i = 0; i < MR_TSC_SYNC_NUM_BEST_ROUNDS; i++) {
+ total_offset = delay_offset[i].offset;
+ }
+ eng->MR_eng_cpu_clock_ticks_offset = total_offset + MR_global_offset;
+
+ MR_DO_THREADSCOPE_DEBUG({
+ fprintf(stderr, "TSC Synchronization offset for thread 0x%x: %ld\n",
+ pthread_self(), eng->MR_eng_cpu_clock_ticks_offset);
+ });
+}
+
+static int
+compare_time_delay_offset_by_delay(const void *a, const void *b) {
+ TimeDelayOffset *tdo_a = (TimeDelayOffset*)a;
+ TimeDelayOffset *tdo_b = (TimeDelayOffset*)b;
+
+ if (tdo_a->delay > tdo_b->delay) {
+ return 1;
+ } else if (tdo_a->delay < tdo_b->delay) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+#endif
+
+/***************************************************************************/
+
+void
+MR_threadscope_post_create_context(MR_Context *context)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+
+ if (!enough_room_for_event(buffer, MR_TS_EVENT_CREATE_THREAD)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_EVENT_CREATE_THREAD,
+ get_current_time_nanosecs());
+ put_context_id(buffer, context->MR_ctxt_num_id);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_reuse_context(MR_Context *context, MR_Unsigned old_id)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_REUSE_THREAD)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_REUSE_THREAD,
+ get_current_time_nanosecs());
+ put_context_id(buffer, context->MR_ctxt_num_id);
+ put_context_id(buffer, old_id);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_create_context_for_spark(MR_Context *context)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+
+ if (!enough_room_for_event(buffer, MR_TS_EVENT_CREATE_SPARK_THREAD)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_EVENT_CREATE_SPARK_THREAD,
+ get_current_time_nanosecs());
+ put_context_id(buffer, context->MR_ctxt_num_id);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_release_context(MR_Context *context)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_RELEASE_CONTEXT)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_RELEASE_CONTEXT,
+ get_current_time_nanosecs());
+ put_context_id(buffer, context->MR_ctxt_num_id);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_context_runnable(MR_Context *context)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+
+ if (!enough_room_for_event(buffer, MR_TS_EVENT_THREAD_RUNNABLE)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_EVENT_THREAD_RUNNABLE,
+ get_current_time_nanosecs());
+ put_context_id(buffer, context->MR_ctxt_num_id);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+static void
+MR_threadscope_post_run_context_locked(
+ struct MR_threadscope_event_buffer *buffer, MR_Context *context)
+{
+ if (!enough_room_for_event(buffer, MR_TS_EVENT_RUN_THREAD)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_thread_engine_base->MR_eng_id);
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_thread_engine_base->MR_eng_id);
+ }
+
+ put_event_header(buffer, MR_TS_EVENT_RUN_THREAD,
+ get_current_time_nanosecs());
+ put_context_id(buffer,
+ MR_thread_engine_base->MR_eng_this_context->MR_ctxt_num_id);
+}
+
+void
+MR_threadscope_post_run_context(void)
+{
+ struct MR_threadscope_event_buffer *buffer;
+ MR_Context *context;
+
+ buffer = MR_thread_engine_base->MR_eng_ts_buffer;
+
+ context = MR_thread_engine_base->MR_eng_this_context;
+
+ if (context) {
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (buffer->MR_tsbuffer_ctxt_is_stopped) {
+ MR_threadscope_post_run_context_locked(buffer, context);
+ buffer->MR_tsbuffer_ctxt_is_stopped = MR_FALSE;
+ }
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+ }
+}
+
+static void
+MR_threadscope_post_stop_context_locked(
+ struct MR_threadscope_event_buffer *buffer,
+ MR_Context *context, MR_ContextStopReason reason)
+{
+ if (!enough_room_for_event(buffer, MR_TS_EVENT_STOP_THREAD)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_thread_engine_base->MR_eng_id);
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_thread_engine_base->MR_eng_id);
+ }
+
+ put_event_header(buffer, MR_TS_EVENT_STOP_THREAD,
+ get_current_time_nanosecs());
+ put_context_id(buffer, context->MR_ctxt_num_id);
+ put_stop_reason(buffer, reason);
+}
+
+void
+MR_threadscope_post_stop_context(MR_ContextStopReason reason)
+{
+ struct MR_threadscope_event_buffer *buffer;
+ MR_Context *context;
+
+ buffer = MR_thread_engine_base->MR_eng_ts_buffer;
+ context = MR_thread_engine_base->MR_eng_this_context;
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!buffer->MR_tsbuffer_ctxt_is_stopped) {
+ MR_threadscope_post_stop_context_locked(buffer, context, reason);
+ buffer->MR_tsbuffer_ctxt_is_stopped = MR_TRUE;
+ }
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_run_spark(MR_SparkId spark_id)
+{
+ struct MR_threadscope_event_buffer *buffer;
+
+ buffer = MR_thread_engine_base->MR_eng_ts_buffer;
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_SPARK_RUN)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_SPARK_RUN,
+ get_current_time_nanosecs());
+ put_spark_id(buffer, spark_id);
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_steal_spark(MR_SparkId spark_id)
+{
+ struct MR_threadscope_event_buffer *buffer;
+ unsigned engine_id;
+
+ buffer = MR_thread_engine_base->MR_eng_ts_buffer;
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_SPARK_STEAL)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_SPARK_STEAL,
+ get_current_time_nanosecs());
+
+ /*
+ ** The engine that created the spark (which may not be whom it was stolen
+ ** from if different work-stealking algorithms are implemented) can be
+ ** derived from the spark id.
+ */
+ engine_id = (spark_id & 0xFF000000) >> 24;
+ put_be_uint16(buffer, engine_id);
+ put_spark_id(buffer, spark_id);
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_sparking(MR_Word* dynamic_conj_id, MR_SparkId spark_id)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_SPARK_CREATE)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_SPARK_CREATE,
+ get_current_time_nanosecs());
+ put_par_conj_dynamic_id(buffer, dynamic_conj_id);
+ put_spark_id(buffer, spark_id);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_calling_main(void)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_CALLING_MAIN)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_CALLING_MAIN,
+ get_current_time_nanosecs());
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_looking_for_global_context(void)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer,
+ MR_TS_MER_EVENT_LOOKING_FOR_GLOBAL_CONTEXT))
+ {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_LOOKING_FOR_GLOBAL_CONTEXT,
+ get_current_time_nanosecs());
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_looking_for_local_spark(void)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_LOOKING_FOR_LOCAL_SPARK)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_LOOKING_FOR_LOCAL_SPARK,
+ get_current_time_nanosecs());
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_work_stealing(void)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_WORK_STEALING)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_WORK_STEALING,
+ get_current_time_nanosecs());
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_start_par_conj(MR_Word* dynamic_id,
+ MR_TS_StringId static_id)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_START_PAR_CONJ)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_START_PAR_CONJ,
+ get_current_time_nanosecs());
+ put_par_conj_dynamic_id(buffer, dynamic_id);
+ put_string_id(buffer, static_id);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_end_par_conj(MR_Word *dynamic_id)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_END_PAR_CONJ)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_END_PAR_CONJ,
+ get_current_time_nanosecs());
+ put_par_conj_dynamic_id(buffer, dynamic_id);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_end_par_conjunct(MR_Word *dynamic_id)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_END_PAR_CONJUNCT)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_END_PAR_CONJUNCT,
+ get_current_time_nanosecs());
+ put_par_conj_dynamic_id(buffer, dynamic_id);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+/*
+** Register a string for use in future messages.
+*/
+static MR_TS_StringId
+MR_threadscope_register_string(const char *string)
+{
+ MR_TS_StringId id;
+ unsigned length;
+
+ length = strlen(string);
+
+ /*
+ ** +2 for the event length.
+ ** +4 for the string id.
+ */
+ if (!enough_room_for_variable_size_event(&global_buffer, strlen(string)
+ + 2 + 4))
+ {
+ flush_event_buffer(&global_buffer);
+ }
+
+ put_event_header(&global_buffer, MR_TS_EVENT_INTERN_STRING, 0);
+ id = MR_next_string_id++;
+ put_be_uint16(&global_buffer, length + 4);
+ put_raw_string(&global_buffer, string, length);
+ put_string_id(&global_buffer, id);
+
+ return id;
+}
+
+void
+MR_threadscope_register_strings_array(MR_Threadscope_String *array,
+ unsigned size)
+{
+ unsigned i;
+
+ for (i = 0; i < size; i++) {
+ array[i].MR_tsstring_id =
+ MR_threadscope_register_string(array[i].MR_tsstring_string);
+ }
+
+ flush_event_buffer(&global_buffer);
+}
+
+void
+MR_threadscope_post_log_msg(const char *message)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_variable_size_event(buffer, strlen(message) + 2)) {
+ flush_event_buffer(buffer),
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_EVENT_LOG_MSG,
+ get_current_time_nanosecs());
+ put_string_size16(buffer, message);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_create_engset(MR_EngSetId id, MR_EngSetType type)
+{
+ struct MR_threadscope_event_buffer *buffer = &global_buffer;
+
+ if (!enough_room_for_event(buffer, MR_TS_EVENT_CAPSET_CREATE)) {
+ flush_event_buffer(buffer);
+ }
+
+ put_event_header(buffer, MR_TS_EVENT_CAPSET_CREATE, 0);
+ put_engset_id(buffer, id);
+ put_engset_type(buffer, type);
+}
+
+void
+MR_threadscope_post_destroy_engset(MR_EngSetId id)
+{
+ struct MR_threadscope_event_buffer *buffer = &global_buffer;
+
+ if (!enough_room_for_event(buffer, MR_TS_EVENT_CAPSET_DELETE)) {
+ flush_event_buffer(buffer);
+ }
+
+ put_event_header(buffer, MR_TS_EVENT_CAPSET_DELETE,
+ get_current_time_nanosecs());
+ put_engset_id(buffer, id);
+}
+
+void
+MR_threadscope_post_engset_add(struct MR_threadscope_event_buffer *buffer,
+ MR_EngSetId id, MR_EngineId eng)
+{
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ maybe_close_block(buffer);
+ if (!enough_room_for_event(buffer, MR_TS_EVENT_CAPSET_ASSIGN_CAP)) {
+ flush_event_buffer(buffer);
+ }
+
+ /*
+ ** When this event occurs the engine hasn't been setup yet. Even though
+ ** we use the engine's buffer, we cannot use get_current_time_nanosecs().
+ */
+ put_event_header(buffer, MR_TS_EVENT_CAPSET_ASSIGN_CAP, 0);
+ put_engset_id(buffer, id);
+ put_engine_id(buffer, eng);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_engset_remove(MR_EngSetId id, MR_EngineId eng)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ maybe_close_block(buffer);
+ if (!enough_room_for_event(buffer, MR_TS_EVENT_CAPSET_REMOVE_CAP)) {
+ flush_event_buffer(buffer);
+ }
+
+ put_event_header(buffer, MR_TS_EVENT_CAPSET_REMOVE_CAP,
+ get_current_time_nanosecs());
+ put_engset_id(buffer, id);
+ put_engine_id(buffer, eng);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_runtime_identifier(MR_EngSetId engset_id,
+ const char *identifier)
+{
+ struct MR_threadscope_event_buffer *buffer = &global_buffer;
+ unsigned len;
+
+ len = strlen(identifier);
+
+ if (!enough_room_for_variable_size_event(buffer, len + SZ_CAPSET_ID)) {
+ flush_event_buffer(buffer);
+ }
+
+ put_event_header(buffer, MR_TS_EVENT_RTS_IDENTIFIER, 0);
+ put_be_int16(buffer, len + SZ_CAPSET_ID);
+ put_engset_id(buffer, engset_id);
+ put_raw_string(buffer, identifier, len);
+}
+
+void
+MR_threadscope_post_new_future(MR_Future *future_id, MR_TS_StringId name)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_FUT_CREATE)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_FUT_CREATE,
+ get_current_time_nanosecs());
+ put_future_id(buffer, future_id);
+ put_string_id(buffer, name);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_wait_future_nosuspend(MR_Future* future_id)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_FUT_WAIT_NOSUSPEND)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_FUT_WAIT_NOSUSPEND,
+ get_current_time_nanosecs());
+ put_future_id(buffer, future_id);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_wait_future_suspended(MR_Future* future_id)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_FUT_WAIT_SUSPENDED)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_FUT_WAIT_SUSPENDED,
+ get_current_time_nanosecs());
+ put_future_id(buffer, future_id);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void
+MR_threadscope_post_signal_future(MR_Future* future_id)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_FUT_SIGNAL)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_FUT_SIGNAL,
+ get_current_time_nanosecs());
+ put_future_id(buffer, future_id);
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+void MR_threadscope_post_engine_sleeping(void)
+{
+ struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
+
+ MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
+ if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_ENGINE_SLEEPING)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_ENGINE(MR_eng_id));
+ }
+
+ put_event_header(buffer, MR_TS_MER_EVENT_ENGINE_SLEEPING,
+ get_current_time_nanosecs());
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+}
+
+/***************************************************************************/
+
+static struct MR_threadscope_event_buffer*
+MR_create_event_buffer(void)
+{
+ struct MR_threadscope_event_buffer* buffer;
+
+ buffer = MR_GC_NEW(MR_threadscope_event_buffer_t);
+ buffer->MR_tsbuffer_pos = 0;
+ buffer->MR_tsbuffer_block_open_pos = -1;
+ buffer->MR_tsbuffer_ctxt_is_stopped = MR_TRUE;
+ buffer->MR_tsbuffer_lock = MR_US_LOCK_INITIAL_VALUE;
+
+ return buffer;
+}
+
+/***************************************************************************/
+
+static void
+MR_open_output_file_and_write_prelude(void)
+{
+ MR_Unsigned filename_len;
+ char *progname_copy;
+ char *progname_base;
+ MR_Unsigned i;
+
+ progname_copy = strdup(MR_progname);
+ progname_base = basename(progname_copy);
+
+ /*
+ ** This is an over-approximation on the amount of space needed
+ ** for this filename.
+ */
+ filename_len = strlen(progname_base) + strlen(MR_TS_FILENAME_FORMAT) + 1;
+ MR_threadscope_output_filename = MR_GC_NEW_ARRAY(char, filename_len);
+ snprintf(MR_threadscope_output_filename, filename_len,
+ MR_TS_FILENAME_FORMAT, progname_base);
+ free(progname_copy);
+ progname_copy = NULL;
+ progname_base = NULL;
+
+ MR_threadscope_output_file = fopen(MR_threadscope_output_filename, "w");
+ if (!MR_threadscope_output_file) {
+ perror(MR_threadscope_output_filename);
+ return;
+ }
+
+ put_be_uint32(&global_buffer, MR_TS_EVENT_HEADER_BEGIN);
+ put_be_uint32(&global_buffer, MR_TS_EVENT_HET_BEGIN);
+ for (i = 0;
+ event_type_descs[i].etd_event_type != MR_TS_NUM_EVENT_TAGS;
+ i++)
+ {
+ put_event_type(&global_buffer, &event_type_descs[i]);
+ }
+ put_be_uint32(&global_buffer, MR_TS_EVENT_HET_END);
+ put_be_uint32(&global_buffer, MR_TS_EVENT_HEADER_END);
+ put_be_uint32(&global_buffer, MR_TS_EVENT_DATA_BEGIN);
+
+ flush_event_buffer(&global_buffer);
+}
+
+static void
+MR_close_output_file(void)
+{
+ if (MR_threadscope_output_file) {
+ put_be_uint16(&global_buffer, MR_TS_EVENT_DATA_END);
+ if (flush_event_buffer(&global_buffer)) {
+ if (EOF == fclose(MR_threadscope_output_file)) {
+ perror(MR_threadscope_output_filename);
+ }
+ MR_threadscope_output_file = NULL;
+ MR_threadscope_output_filename = NULL;
+ }
+ }
+}
+
+static void
+put_event_type(struct MR_threadscope_event_buffer *buffer,
+ EventTypeDesc *event_type_desc)
+{
+ MR_int_least16_t size;
+ EventType event_type;
+
+ /* This also fills in our tables of event sizes. */
+ event_type = event_type_desc->etd_event_type;
+ size = event_type_desc->etd_size;
+ if (event_type < MR_TS_NUM_EVENT_TAGS) {
+ event_type_sizes[event_type] = size;
+ } else if ((event_type < (MR_TS_MER_EVENT_START + MR_TS_NUM_MER_EVENTS))
+ && (event_type >= MR_TS_MER_EVENT_START))
+ {
+ event_type_sizes_mercury[event_type - MR_TS_MER_EVENT_START] = size;
+ } else {
+ fprintf(stderr, "Unknown event type %d\n", event_type);
+ abort();
+ }
+
+ put_be_uint32(buffer, MR_TS_EVENT_ET_BEGIN);
+
+ put_be_uint16(buffer, event_type);
+ put_be_int16(buffer, size);
+
+ put_string_size32(buffer, event_type_desc->etd_description);
+
+ if (event_type_desc->edt_extends_event != 0xFFFF) {
+ put_be_uint32(buffer, 2 + 2 + SZ_EVENT_TYPE);
+ put_be_uint16(buffer, MR_EXT_TYPE_EXTENSION);
+ put_be_uint16(buffer, SZ_EVENT_TYPE);
+ put_be_uint16(buffer, event_type_desc->edt_extends_event);
+ } else {
+ /* There is no extended data in this event */
+ put_be_uint32(buffer, 0);
+ }
+
+ put_be_uint32(buffer, MR_TS_EVENT_ET_END);
+}
+
+static MR_bool
+flush_event_buffer(struct MR_threadscope_event_buffer *buffer)
+{
+ maybe_close_block(buffer);
+
+ /*
+ ** fwrite handles locking for us, so we have no concurrent access problems.
+ */
+ if (MR_threadscope_output_file && buffer->MR_tsbuffer_pos) {
+ if (0 == fwrite(buffer->MR_tsbuffer_data, buffer->MR_tsbuffer_pos, 1,
+ MR_threadscope_output_file))
+ {
+ perror(MR_threadscope_output_filename);
+ MR_threadscope_output_file = NULL;
+ MR_threadscope_output_filename = NULL;
+ }
+ }
+ buffer->MR_tsbuffer_pos = 0;
+
+ return (MR_threadscope_output_filename ? MR_TRUE : MR_FALSE);
+}
+
+static void
+maybe_close_block(struct MR_threadscope_event_buffer *buffer)
+{
+ MR_Unsigned saved_pos;
+
+ if (buffer->MR_tsbuffer_block_open_pos != -1) {
+ saved_pos = buffer->MR_tsbuffer_pos;
+ buffer->MR_tsbuffer_pos = buffer->MR_tsbuffer_block_open_pos +
+ sizeof(EventType) + sizeof(Time);
+ put_eventlog_offset(buffer,
+ saved_pos - buffer->MR_tsbuffer_block_open_pos);
+ put_timestamp(buffer, get_current_time_nanosecs());
+
+ buffer->MR_tsbuffer_block_open_pos = -1;
+ buffer->MR_tsbuffer_pos = saved_pos;
+ }
+}
+
+static void
+open_block(struct MR_threadscope_event_buffer *buffer, MR_Unsigned eng_id)
+{
+ maybe_close_block(buffer);
+
+ /*
+ ** Save the old position. Close block uses this so that it knows
+ ** where the block marker is that it should write into.
+ */
+ buffer->MR_tsbuffer_block_open_pos = buffer->MR_tsbuffer_pos;
+
+ put_event_header(buffer, MR_TS_EVENT_BLOCK_MARKER,
+ get_current_time_nanosecs());
+
+ /* Skip over the next two fields, they are filled in by close_block. */
+ buffer->MR_tsbuffer_pos += sizeof(EventlogOffset) + sizeof(Time);
+
+ put_engine_id(buffer, eng_id);
+}
+
+static void
+start_gc_callback(void)
+{
+ struct MR_threadscope_event_buffer *buffer;
+ MR_Context *context;
+
+ MR_DO_THREADSCOPE_DEBUG(
+ fprintf(stderr, "In gc start callback thread: 0x%lx\n", pthread_self())
+ );
+ if (MR_thread_engine_base == NULL) {
+ return;
+ }
+ MR_DO_THREADSCOPE_DEBUG(
+ fprintf(stderr, "\tEngine: 0x%.16lx\n", MR_thread_engine_base)
+ );
+ buffer = MR_thread_engine_base->MR_eng_ts_buffer;
+ if (buffer == NULL) {
+ /* GC might be running before we're done setting up */
+ return;
+ }
+ MR_DO_THREADSCOPE_DEBUG(
+ fprintf(stderr, "\tBuffer: 0x%.16lx\n", buffer)
+ );
+
+ if (MR_US_TRY_LOCK(&(buffer->MR_tsbuffer_lock))) {
+ context = MR_thread_engine_base->MR_eng_this_context;
+ if (!buffer->MR_tsbuffer_ctxt_is_stopped && context) {
+ MR_threadscope_post_stop_context_locked(buffer,
+ context, MR_TS_STOP_REASON_HEAP_OVERFLOW);
+ }
+
+ if (!enough_room_for_event(buffer, MR_TS_EVENT_GC_START)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_thread_engine_base->MR_eng_id);
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_thread_engine_base->MR_eng_id);
+ }
+
+ put_event_header(buffer, MR_TS_EVENT_GC_START,
+ get_current_time_nanosecs());
+
+ if (!enough_room_for_event(buffer, MR_TS_EVENT_GC_GLOBAL_SYNC)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_thread_engine_base->MR_eng_id);
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_thread_engine_base->MR_eng_id);
+ }
+
+ /*
+ ** Idealy this event should be posted after the world has stopped.
+ ** Doing so means adding more instrumentation into Boehm.
+ */
+ put_event_header(buffer, MR_TS_EVENT_GC_GLOBAL_SYNC,
+ get_current_time_nanosecs());
+
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+ }
+}
+
+static void
+stop_gc_callback(void)
+{
+ struct MR_threadscope_event_buffer *buffer;
+ MR_Context *context;
+
+ MR_DO_THREADSCOPE_DEBUG(
+ fprintf(stderr, "In gc stop callback thread: 0x%lx\n", pthread_self());
+ );
+ if (MR_thread_engine_base == NULL) return;
+ buffer = MR_thread_engine_base->MR_eng_ts_buffer;
+ if (buffer == NULL) {
+ /* GC might be running before we're done setting up */
+ return;
+ }
+
+ if (MR_US_TRY_LOCK(&(buffer->MR_tsbuffer_lock))) {
+ if (!enough_room_for_event(buffer, MR_TS_EVENT_GC_END)) {
+ flush_event_buffer(buffer);
+ open_block(buffer, MR_thread_engine_base->MR_eng_id);
+ } else if (!block_is_open(buffer)) {
+ open_block(buffer, MR_thread_engine_base->MR_eng_id);
+ }
+
+ put_event_header(buffer, MR_TS_EVENT_GC_END,
+ get_current_time_nanosecs());
+
+ context = MR_thread_engine_base->MR_eng_this_context;
+ if (!buffer->MR_tsbuffer_ctxt_is_stopped && context) {
+ MR_threadscope_post_run_context_locked(buffer, context);
+ }
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+ }
+}
+
+static void
+pause_thread_gc_callback(void)
+{
+ struct MR_threadscope_event_buffer *buffer;
+ MR_Context *context;
+
+ MR_DO_THREADSCOPE_DEBUG(
+ fprintf(stderr, "In gc pause thread callback thread: 0x%lx\n",
+ pthread_self())
+ );
+ if (MR_thread_engine_base == NULL) return;
+ buffer = MR_thread_engine_base->MR_eng_ts_buffer;
+ if (buffer == NULL) {
+ /* GC might be running before we're done setting up */
+ return;
+ }
+
+ if (MR_US_TRY_LOCK(&(buffer->MR_tsbuffer_lock))) {
+ context = MR_thread_engine_base->MR_eng_this_context;
+ if (!buffer->MR_tsbuffer_ctxt_is_stopped && context) {
+ MR_threadscope_post_stop_context_locked(buffer, context,
+ MR_TS_STOP_REASON_YIELDING);
+ }
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+ }
+}
+
+static void
+resume_thread_gc_callback(void)
+{
+ struct MR_threadscope_event_buffer *buffer;
+ MR_Context *context;
+
+ MR_DO_THREADSCOPE_DEBUG(
+ fprintf(stderr, "In gc resume thread callback thread: 0x%lx\n",
+ pthread_self());
+ );
+ if (MR_thread_engine_base == NULL) return;
+ buffer = MR_thread_engine_base->MR_eng_ts_buffer;
+ if (buffer == NULL) {
+ /* GC might be running before we're done setting up */
+ return;
+ }
+
+ if (MR_US_TRY_LOCK(&(buffer->MR_tsbuffer_lock))) {
+ context = MR_thread_engine_base->MR_eng_this_context;
+ if (!buffer->MR_tsbuffer_ctxt_is_stopped && context) {
+ MR_threadscope_post_run_context_locked(buffer, context);
+ }
+ MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
+ }
+}
+
+/***************************************************************************/
+
+static Time
+get_current_time_nanosecs(void)
+{
+ if (MR_threadscope_use_tsc) {
+ MR_uint_least64_t current_tsc;
+ MercuryEngine *eng = MR_thread_engine_base;
+
+ current_tsc = MR_read_cpu_tsc();
+
+ /* The large constant (10^9) here converts seconds into nanoseconds. */
+ return (current_tsc + eng->MR_eng_cpu_clock_ticks_offset) /
+ (MR_cpu_cycles_per_sec / 1000000000);
+ } else {
+ return gettimeofday_nsecs() + MR_gettimeofday_offset;
+ }
+}
+
+static Time
+gettimeofday_nsecs(void)
+{
+ struct timeval tv;
+
+ if (0 != gettimeofday(&tv, NULL)) {
+ perror("gettimeofday()");
+ /*
+ ** Return a stupid value generating an obviously bad logfile
+ ** rather than crashing a program that may otherwise work.
+ */
+ return 0;
+ }
+ return (Time)tv.tv_sec * 1000000000 +
+ (Time)tv.tv_usec * 1000;
+}
+
+/***************************************************************************/
+
+#endif /* MR_THREADSCOPE */
diff --git a/runtime/mercury_par_profile.h b/runtime/mercury_par_profile.h
new file mode 100644
index 0000000..b9c43ff
--- /dev/null
+++ b/runtime/mercury_par_profile.h
@@ -0,0 +1,242 @@
+/*
+** vim:ts=4 sw=4 expandtab
+*/
+/*
+** Copyright (C) 2009-2011,2013 The University of Melbourne.
+**
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING.LIB in the Mercury distribution.
+*/
+
+/*
+** mercury_par_profile.h - defines Mercury threadscope profiling support.
+**
+** See "Parallel Preformance Tuning for Haskell" - Don Jones Jr, Simon Marlow
+** and Satnam Singh for information about threadscope.
+*/
+
+#ifndef MERCURY_PAR_PROFILE_H
+#define MERCURY_PAR_PROFILE_H
+
+#include "mercury_types.h" /* for MR_Word, MR_Code, etc */
+#include "mercury_engine.h"
+#include "mercury_context.h"
+
+#ifdef MR_THREADSCOPE
+
+/*
+** Reasons why a context has been stopped, not all of these apply to Mercury,
+** for instance contexts don't yield.
+*/
+#define MR_TS_STOP_REASON_HEAP_OVERFLOW 1
+#define MR_TS_STOP_REASON_STACK_OVERFLOW 2
+#define MR_TS_STOP_REASON_YIELDING 3
+#define MR_TS_STOP_REASON_BLOCKED 4
+#define MR_TS_STOP_REASON_FINISHED 5
+
+typedef struct MR_threadscope_event_buffer MR_threadscope_event_buffer_t;
+
+typedef MR_uint_least16_t MR_ContextStopReason;
+typedef MR_Integer MR_ContextId;
+typedef MR_uint_least32_t MR_TS_StringId;
+typedef MR_uint_least32_t MR_SparkId;
+typedef MR_uint_least32_t MR_EngSetId;
+typedef MR_uint_least16_t MR_EngSetType;
+typedef MR_uint_least32_t MR_TS_Pid;
+
+typedef struct MR_Threadscope_String {
+ const char* MR_tsstring_string;
+ MR_TS_StringId MR_tsstring_id;
+} MR_Threadscope_String;
+
+/*
+** Set this to true to use the CPU's time stamp counter.
+**
+** This is initially set in mercury_wrapper.c and may be reset by
+** MR_setup_threadscope if the TSC can't be used.
+*/
+extern MR_bool MR_threadscope_use_tsc;
+
+/*
+** This must be called by the primordial thread before starting any other
+** threads but after the primordial thread has been pinned.
+*/
+extern void MR_setup_threadscope(void);
+
+extern void MR_finalize_threadscope(void);
+
+extern void MR_threadscope_setup_engine(MercuryEngine *eng);
+
+extern void MR_threadscope_finalize_engine(MercuryEngine *eng);
+
+#if 0
+/*
+** It looks like we don't need TSC synchronization code on modern x86(-64) CPUs
+** including multi-socket systems (tested on goliath and taura). If we find
+** systems where this is needed we can enable it via a runtime check.
+*/
+/*
+** Synchronize a slave thread's TSC offset to the master's. The master thread
+** (with an engine) should call MR_threadscope_sync_tsc_master() for each slave
+** while each slave (with an engine) calls MR_threadscope_sync_tsc_slave().
+** All master - slave pairs must be pinned to CPUs and setup their threadscope
+** structures already (by calling MR_threadscope_setup_engine() above).
+** Multiple slaves may call the _slave at the same time, a lock is used to
+** synchronize only one at a time. Only the primordial thread may call
+** MR_threadscope_sync_tsc_master().
+*/
+extern void MR_threadscope_sync_tsc_master(void);
+extern void MR_threadscope_sync_tsc_slave(void);
+#endif
+
+/*
+** Use the following functions to post messages. All messages will read the
+** current engine's ID from the engine word, some messages will also read the
+** current context id from the context loaded into the current engine.
+*/
+
+/*
+** This context has been created, The context must be passed as a parameter so
+** that it doesn't have to be the current context.
+**
+** Using the MR_Context typedef here requires the inclusion of
+** mercury_context.h, creating a circular dependency
+*/
+extern void MR_threadscope_post_create_context(
+ struct MR_Context_Struct *context);
+
+/*
+** The given context was created in order to execute a spark. This
+** event should be posted in addition to (and after) create_thread
+** above.
+*/
+extern void MR_threadscope_post_create_context_for_spark(
+ struct MR_Context_Struct *ctxt);
+
+/*
+** This context is being released (back into a pool of free contexts). We may
+** see a new create_context or create_context_for_spark message with the same
+** context ID, such a message indicates that the context is being re-used.
+*/
+extern void MR_threadscope_post_release_context(
+ struct MR_Context_Struct *context);
+
+/*
+** This context is being reused (after being released). This event is an
+** alternative to create_context above.
+*/
+extern void MR_threadscope_post_reuse_context(
+ struct MR_Context_Struct *context, MR_Unsigned old_id);
+
+/*
+** This message says the context is now ready to run. Such as it's being
+** placed on the run queue after being blocked
+*/
+extern void MR_threadscope_post_context_runnable(
+ struct MR_Context_Struct *context);
+
+/*
+** This message says we're now running the current context
+*/
+extern void MR_threadscope_post_run_context(void);
+
+/*
+** This message says we've stopped executing the current context,
+** a reason why should be provided.
+*/
+extern void MR_threadscope_post_stop_context(MR_ContextStopReason reason);
+
+/*
+** This message says we're about to execute a spark from our local stack.
+*/
+extern void MR_threadscope_post_run_spark(MR_SparkId spark_id);
+
+/*
+** This message says that we're about to execute a spark that was stolen from
+** another's stack.
+*/
+extern void MR_threadscope_post_steal_spark(MR_SparkId spark_id);
+
+/*
+** This message says that a spark is being created for the given computation.
+** The spark's ID is given as an argument.
+*/
+extern void MR_threadscope_post_sparking(MR_Word* dynamic_conj_id,
+ MR_SparkId spark_id);
+
+/*
+** Post this message just before invoking the main/2 predicate.
+*/
+extern void MR_threadscope_post_calling_main(void);
+
+/*
+** Post this message when an engine begins looking for a context to run.
+*/
+extern void MR_threadscope_post_looking_for_global_context(void);
+
+/*
+** Post this message when an engine begins trying to run a spark from it's
+** local stack.
+*/
+extern void MR_threadscope_post_looking_for_local_spark(void);
+
+/*
+** Post this message when a thread is about to attempt work stealing.
+*/
+extern void MR_threadscope_post_work_stealing(void);
+
+/*
+** Post this message before a parallel conjunction starts.
+*/
+extern void MR_threadscope_post_start_par_conj(MR_Word* dynamic_id,
+ MR_TS_StringId static_id);
+
+/*
+** Post this message after a parallel conjunction stops.
+*/
+extern void MR_threadscope_post_end_par_conj(MR_Word* dynamic_id);
+
+/*
+** Post this message when a parallel conjunct calls the bariier code.
+*/
+extern void MR_threadscope_post_end_par_conjunct(MR_Word* dynamic_id);
+
+/*
+** Post this message when a future is created, this establishes the conjuction
+** id to future id mapping. The conjunction id is inferred by context.
+** The name of the future within the conjunction is given by 'name'.
+*/
+extern void MR_threadscope_post_new_future(MR_Future* future_id, MR_TS_StringId name);
+
+/*
+** Post either of these messages when waiting on a future. THe first if the
+** context had to be suspended because the future was not available, and the
+** second when the context did not need to be suspended.
+*/
+extern void MR_threadscope_post_wait_future_nosuspend(MR_Future* future_id);
+extern void MR_threadscope_post_wait_future_suspended(MR_Future* future_id);
+
+/*
+** Post this event when signaling the production of a future.
+*/
+extern void MR_threadscope_post_signal_future(MR_Future* future_id);
+
+/*
+** Post this event when the engine is going to sleep.
+*/
+extern void MR_threadscope_post_engine_sleeping(void);
+
+/*
+** Register all the strings in an array and save their IDs in the array.
+*/
+extern void MR_threadscope_register_strings_array(MR_Threadscope_String *array,
+ unsigned size);
+
+/*
+** Post a user-defined log message.
+*/
+extern void MR_threadscope_post_log_msg(const char *message);
+
+#endif /* MR_THREADSCOPE */
+
+#endif /* not MERCURY_PAR_PROFILE_H */
diff --git a/runtime/mercury_thread.c b/runtime/mercury_thread.c
index 99b5f68..780f19a 100644
--- a/runtime/mercury_thread.c
+++ b/runtime/mercury_thread.c
@@ -13,7 +13,7 @@
#include "mercury_memory.h"
#include "mercury_context.h" /* for MR_do_runnext */
#include "mercury_thread.h"
-#include "mercury_threadscope.h"
+#include "mercury_par_profile.h"
#include <stdio.h>
#include <errno.h>
@@ -182,7 +182,7 @@ MR_init_thread(MR_when_to_use when_to_use)
#ifdef MR_THREADSCOPE
/*
** TSC Synchronization is not used, support is commented out. See
- ** runtime/mercury_threadscope.h for an explanation.
+ ** runtime/mercury_par_profiling.h for an explanation.
**
if (when_to_use == MR_use_later) {
MR_threadscope_sync_tsc_slave();
diff --git a/runtime/mercury_threadscope.c b/runtime/mercury_threadscope.c
deleted file mode 100644
index cda565c..0000000
--- a/runtime/mercury_threadscope.c
+++ /dev/null
@@ -1,2269 +0,0 @@
-/*
-** vim: ts=4 sw=4 expandtab
-*/
-/*
-** Copyright (C) 2009-2011 The University of Melbourne.
-** Copyright (C) 2008-2009 The GHC Team.
-**
-** This file may only be copied under the terms of the GNU Library General
-** Public License - see the file COPYING.LIB in the Mercury distribution.
-*/
-
-/*
-** Event log format
-**
-** The log format is designed to be extensible: old tools should be able
-** to parse (but not necessarily understand) new versions of the format,
-** and new tools will be able to understand old log files.
-**
-** Each event has a specific format. If you add new events, give them
-** new numbers: we never re-use old event numbers.
-**
-** - The format is endian-independent: all values are represented in
-** bigendian order.
-**
-** - The format is extensible:
-**
-** - The header describes each event type and its length. Tools that
-** don't recognise a particular event type can skip those events.
-**
-** - There is room for extra information in the event type
-** specification, which can be ignored by older tools.
-**
-** - Events can have extra information added, but existing fields
-** cannot be changed. Tools should ignore extra fields at the
-** end of the event record.
-**
-** - Old event type ids are never re-used; just take a new identifier.
-**
-**
-** The format
-** ----------
-**
-** log : EVENT_HEADER_BEGIN
-** EventType*
-** EVENT_HEADER_END
-** EVENT_DATA_BEGIN
-** Event*
-** EVENT_DATA_END
-**
-** EventType :
-** EVENT_ET_BEGIN
-** Word16 -- unique identifier for this event
-** Int16 -- >=0 size of the event in bytes (minus the header)
-** -- -1 variable size
-** Word32 -- length of the next field in bytes
-** Word8* -- string describing the event
-** Word32 -- length of the next field in bytes
-** EventTypeExt* -- extensions
-** EVENT_ET_END
-**
-** Event :
-** Word16 -- event_type
-** Word64 -- time (nanosecs)
-** [Word16] -- length of the rest (for variable-sized events only)
-** ... extra event-specific info ...
-**
-** EventTypeExt :
-** Word16 -- unique identifier for this extension type.
-** Word16 -- size of the payload in bytes.
-** Word8 -- payload bytes, their meaning depends upon the type.
-**
-** EVENT_EXT_TYPE_EXTENSION
-** This event extends another event also defined in this file, the payload of
-** this extension is:
-** Word16 -- unique identifier of the event being extended
-**
-** All values are packed, no attempt is made to align them.
-**
-** New events must be registered with GHC. These are kept in the GHC-events
-** package.
-**
-*/
-
-#include "mercury_imp.h"
-#include "mercury_threadscope.h"
-#include "mercury_atomic_ops.h"
-
-#include <stdio.h>
-#include <string.h>
-
-#ifdef MR_THREADSCOPE
-
-/***************************************************************************/
-
-/*
-** Markers for begin/end of the Header.
-*/
-#define MR_TS_EVENT_HEADER_BEGIN 0x68647262 /* 'h' 'd' 'r' 'b' */
-#define MR_TS_EVENT_HEADER_END 0x68647265 /* 'h' 'd' 'r' 'e' */
-
-#define MR_TS_EVENT_DATA_BEGIN 0x64617462 /* 'd' 'a' 't' 'b' */
-#define MR_TS_EVENT_DATA_END 0xffff
-
-/*
-** Markers for begin/end of the list of Event Types in the Header.
-** Header, Event Type, Begin = hetb
-** Header, Event Type, End = hete
-*/
-#define MR_TS_EVENT_HET_BEGIN 0x68657462 /* 'h' 'e' 't' 'b' */
-#define MR_TS_EVENT_HET_END 0x68657465 /* 'h' 'e' 't' 'e' */
-
-/*
-** Markers for the beginning and end of individual event types.
-*/
-#define MR_TS_EVENT_ET_BEGIN 0x65746200 /* 'e' 't' 'b' 0 */
-#define MR_TS_EVENT_ET_END 0x65746500 /* 'e' 't' 'e' 0 */
-
-/*
-** The threadscope events:
-*/
-#define MR_TS_EVENT_CREATE_THREAD 0 /* (thread) */
-#define MR_TS_EVENT_RUN_THREAD 1 /* (thread) */
-#define MR_TS_EVENT_STOP_THREAD 2 /* (thread, status) */
-#define MR_TS_EVENT_THREAD_RUNNABLE 3 /* (thread) */
-#define MR_TS_EVENT_MIGRATE_THREAD 4 /* (thread, new_cap) */
-#define MR_TS_EVENT_SHUTDOWN 7 /* () */
-#define MR_TS_EVENT_THREAD_WAKEUP 8 /* (thread, other_cap) */
-#define MR_TS_EVENT_GC_START 9 /* () */
-#define MR_TS_EVENT_GC_END 10 /* () */
-#define MR_TS_EVENT_REQUEST_SEQ_GC 11 /* () */
-#define MR_TS_EVENT_REQUEST_PAR_GC 12 /* () */
-#define MR_TS_EVENT_CREATE_SPARK_THREAD 15 /* (spark_thread) */
-#define MR_TS_EVENT_LOG_MSG 16 /* (message ...) */
-#define MR_TS_EVENT_STARTUP 17 /* (num_capabilities) */
-#define MR_TS_EVENT_BLOCK_MARKER 18 /* (size, end_time, capability) */
-#define MR_TS_EVENT_USER_MSG 19 /* (message ...) */
-#define MR_TS_EVENT_GC_IDLE 20 /* () */
-#define MR_TS_EVENT_GC_WORK 21 /* () */
-#define MR_TS_EVENT_GC_DONE 22 /* () */
-
-/* 23, 24 used by eden */
-
-/*
-** Capsets or capability sets are groups of engines with some association,
-** for instance a group of threads in a process.
-*/
-#define MR_TS_EVENT_CAPSET_CREATE 25 /* (capset, capset_type) */
-#define MR_TS_EVENT_CAPSET_DELETE 26 /* (capset) */
-#define MR_TS_EVENT_CAPSET_ASSIGN_CAP 27 /* (capset, cap) */
-#define MR_TS_EVENT_CAPSET_REMOVE_CAP 28 /* (capset, cap) */
-/* the RTS identifier is in the form of "GHC-version rts_way" */
-#define MR_TS_EVENT_RTS_IDENTIFIER 29 /* (capset, name_version_string) */
-/* the vectors in two these events are null separated strings */
-#define MR_TS_EVENT_PROGRAM_ARGS 30 /* (capset, commandline_vector) */
-#define MR_TS_EVENT_PROGRAM_ENV 31 /* (capset, environment_vector) */
-
-#define MR_TS_EVENT_OSPROCESS_PID 32 /* (capset, pid) */
-#define MR_TS_EVENT_OSPROCESS_PPID 33 /* (capset, parent_pid) */
-#define MR_TS_EVENT_SPARK_COUNTERS 34 /* (crt,dud,ovf,cnv,fiz,gcd,rem) */
-#define MR_TS_EVENT_SPARK_CREATE 35 /* () */
-#define MR_TS_EVENT_SPARK_DUD 36 /* () */
-#define MR_TS_EVENT_SPARK_OVERFLOW 37 /* () */
-#define MR_TS_EVENT_SPARK_RUN 38 /* () */
-#define MR_TS_EVENT_SPARK_STEAL 39 /* (victim_cap) */
-#define MR_TS_EVENT_SPARK_FIZZLE 40 /* () */
-#define MR_TS_EVENT_SPARK_GC 41 /* () */
-#define MR_TS_EVENT_INTERN_STRING 42 /* (string, id) */
-#define MR_TS_EVENT_WALL_CLOCK_TIME 43 /* (capset, unix_epoch_seconds, nanoseconds) */
-#define MR_TS_EVENT_THREAD_LABEL 44 /* (thread, name_string) */
-#define MR_TS_EVENT_CAP_CREATE 45 /* (cap) */
-#define MR_TS_EVENT_CAP_DELETE 46 /* (cap) */
-#define MR_TS_EVENT_CAP_DISABLE 47 /* (cap) */
-#define MR_TS_EVENT_CAP_ENABLE 48 /* (cap) */
-#define MR_TS_EVENT_HEAP_ALLOCATED 49 /* (heap_capset, alloc_bytes) */
-#define MR_TS_EVENT_HEAP_SIZE 50 /* (heap_capset, size_bytes) */
-#define MR_TS_EVENT_HEAP_LIVE 51 /* (heap_capset, live_bytes) */
-#define MR_TS_EVENT_HEAP_INFO_GHC 52 /* (heap_capset, n_generations,
- max_heap_size, alloc_area_size,
- mblock_size, block_size) */
-#define MR_TS_EVENT_GC_STATS_GHC 53 /* (heap_capset, generation,
- copied_bytes, slop_bytes, frag_bytes,
- par_n_threads,
- par_max_copied, par_tot_copied) */
-#define MR_TS_EVENT_GC_GLOBAL_SYNC 54 /* () */
-
-#define MR_TS_NUM_EVENT_TAGS 55
-
-#define MR_TS_MER_EVENT_START 100
-
-#define MR_TS_MER_EVENT_START_PAR_CONJ 100 /* (int id, memo'd string id) */
-#define MR_TS_MER_EVENT_END_PAR_CONJ 101 /* (int id) */
-#define MR_TS_MER_EVENT_END_PAR_CONJUNCT 102 /* (int id) */
-
-/*
-** Creating sparks is not specifically mercury, but conjunct IDs are.
-** If other systems wish to use this event, they can move it
-** to the main events section.
-*/
-#define MR_TS_MER_EVENT_SPARK_CREATE 103 /* (int id, spark id) */
-
-#define MR_TS_MER_EVENT_FUT_CREATE 104 /* (fut id, memo'd name id) */
-#define MR_TS_MER_EVENT_FUT_WAIT_NOSUSPEND 105 /* (fut id) */
-#define MR_TS_MER_EVENT_FUT_WAIT_SUSPENDED 106 /* (fut id) */
-#define MR_TS_MER_EVENT_FUT_SIGNAL 107 /* (fut id) */
-#define MR_TS_MER_EVENT_LOOKING_FOR_GLOBAL_CONTEXT \
- 108 /* () */
-#define MR_TS_MER_EVENT_WORK_STEALING 109 /* () */
-#define MR_TS_MER_EVENT_RELEASE_CONTEXT 110 /* (context id) */
-#define MR_TS_MER_EVENT_ENGINE_SLEEPING 111 /* () */
-#define MR_TS_MER_EVENT_LOOKING_FOR_LOCAL_SPARK \
- 112 /* () */
-#define MR_TS_MER_EVENT_CALLING_MAIN 113 /* () */
-#define MR_TS_MER_EVENT_SPARK_RUN 114 /* (spark id) */
-#define MR_TS_MER_EVENT_SPARK_STEAL 115 /* (victim cap, spark id) */
-#define MR_TS_MER_EVENT_REUSE_THREAD 116 /* (context id, old context id) */
-#define MR_TS_NUM_MER_EVENTS 17
-
-#if 0 /* DEPRECATED EVENTS: */
-#define EVENT_CREATE_SPARK 13 /* (cap, thread) */
-#define EVENT_SPARK_TO_THREAD 14 /* (cap, thread, spark_thread) */
-#define MR_TS_EVENT_RUN_SPARK 5 /* (thread, spark_id) */
-#define MR_TS_EVENT_STEAL_SPARK 6 /* (thread, victim_cap, spark_id) */
-#endif
-
-/*
-** Engine set type values for EVENT_CAPSET_CREATE.
-*/
-#define MR_TS_ENGSET_TYPE_CUSTOM 1 /* reserved for end-user applications */
-#define MR_TS_ENGSET_TYPE_OSPROCESS 2 /* engines belong to same OS process */
-#define MR_TS_ENGSET_TYPE_CLOCKDOMAIN 3 /* engines share a local clock/time */
-
-/*
-** Event extension types
-*/
-#define MR_EXT_TYPE_EXTENSION 1 /* This event extends another event */
-
-/*
-** GHC uses 2MB per buffer. Note that the minimum buffer size is the size of
-** the largest message plus the size of the block marker message, however it is
-** _sensible_ for the buffer to be much larger so that we make system calls
-** less often.
-*/
-#define MR_TS_BUFFERSIZE (2*1024*1024)
-#define MR_TS_FILENAME_FORMAT ("%s.eventlog")
-#define MR_TSC_SYNC_NUM_ROUNDS (10)
-#define MR_TSC_SYNC_NUM_BEST_ROUNDS (3)
-
-/* Uncomment this to enable some debugging code */
-/* #define MR_DEBUG_THREADSCOPE 1 */
-
-#if MR_DEBUG_THREADSCOPE
-#define MR_DO_THREADSCOPE_DEBUG(x) do { x; } while(0)
-#else
-#define MR_DO_THREADSCOPE_DEBUG(x)
-#endif
-
-/***************************************************************************/
-
-struct MR_threadscope_event_buffer {
- unsigned char MR_tsbuffer_data[MR_TS_BUFFERSIZE];
-
- /* The current writing position in the buffer. */
- MR_Unsigned MR_tsbuffer_pos;
-
- /* The position of the start of the most recent block. */
- MR_Integer MR_tsbuffer_block_open_pos;
-
- /*
- ** True if the engine's current context is stopped, and therefore
- ** stop and start events should not be posted from the GC callback
- ** procedures.
- */
- MR_bool MR_tsbuffer_ctxt_is_stopped;
-
- /* A cheap userspace lock to make buffers reentrant. */
- volatile MR_Us_Lock MR_tsbuffer_lock;
-};
-
-/*
-** We define some types and functions to write them.
-** These types are set carefully to match the ones that GHC uses.
-*/
-typedef MR_uint_least16_t EventType;
-typedef MR_uint_least64_t Time;
-typedef MR_int_least64_t Timedelta;
-
-/*
-** The difference between two positions in the eventlog file measured in bytes.
-*/
-typedef MR_uint_least32_t EventlogOffset;
-
-/*
-** A descriptor used when writing the header of threadscope files.
-** The fields are:
-**
-** edt_event_type The type of this event
-**
-** edt_description A string description of this event
-**
-** edt_size The event's size or -1 for variable length
-**
-** edt_extends_event The event that this event extends, or 0xFFFF if this is
-** a base event
-*/
-typedef struct {
- EventType etd_event_type;
- const char *etd_description;
- MR_int_least16_t etd_size;
- EventType edt_extends_event;
-} EventTypeDesc;
-
-/***************************************************************************/
-
-#define SZ_EVENT_TYPE 2
-#define SZ_CAPSET_ID 4
-#define SZ_CAPSET_TYPE 2
-#define SZ_CONTEXT_ID 4
-#define SZ_CONTEXT_STOP_REASON 2
-#define SZ_DYN_CONJ_ID 8
-#define SZ_ENGINELOG_OFFSET 4
-#define SZ_ENGINE_ID 2
-#define SZ_PID 4
-#define SZ_SPARK_ID 4
-#define SZ_STRING_ID 4
-#define SZ_STATIC_CONJ_ID (SZ_STRING_ID)
-#define SZ_VAR_NAME_ID (SZ_STRING_ID)
-#define SZ_TIME 8
-#define SZ_FUTURE_ID 8
-
-static EventTypeDesc event_type_descs[] = {
- {
- /*
- ** The startup event informs threadscope of the number of engines
- ** we are using. It should be given outside of a block.
- */
- MR_TS_EVENT_STARTUP,
- "Startup (num_engines)",
- SZ_ENGINE_ID,
- 0xFFFF
- },
- {
- /*
- ** The last event in the log. It should be given outside of a block.
- */
- MR_TS_EVENT_SHUTDOWN,
- "Shutdown",
- 0,
- 0xFFFF
- },
- {
- /*
- ** A block of events belonging to the named engine follows.
- ** The length of this block is given including the block message
- ** itself, the time that this block finishes is also given.
- ** Blocks _must not_ exist within other blocks.
- */
- MR_TS_EVENT_BLOCK_MARKER,
- "A block of events generated by a specific engine follows",
- SZ_ENGINELOG_OFFSET + SZ_TIME + SZ_ENGINE_ID,
- 0xFFFF
- },
- {
- /*
- ** Called when a context is created or re-used.
- */
- MR_TS_EVENT_CREATE_THREAD,
- "A context is created or re-used",
- SZ_CONTEXT_ID,
- 0xFFFF
- },
- {
- /*
- ** Called from MR_schedule_context()
- */
- MR_TS_EVENT_THREAD_RUNNABLE,
- "The context is being placed on the run queue",
- SZ_CONTEXT_ID,
- 0xFFFF
- },
- {
- /*
- ** The engine has taken a spark from it's local stack and will run it.
- */
- MR_TS_EVENT_SPARK_RUN,
- "Run a spark from the local stack",
- 0,
- 0xFFFF
- },
- {
- MR_TS_MER_EVENT_SPARK_RUN,
- "Run a spark from the local stack, the spark is identified by an id",
- SZ_SPARK_ID,
- MR_TS_EVENT_SPARK_RUN
- },
- {
- /*
- ** The named context has begun executing a spark from another
- ** engine's stack.
- */
- MR_TS_EVENT_SPARK_STEAL,
- "Run a spark stolen from another engine",
- SZ_ENGINE_ID,
- 0XFFFF
- },
- {
- MR_TS_MER_EVENT_SPARK_STEAL,
- "Run a spark stolen from another engine, "
- "the spark is identified by an id",
- SZ_ENGINE_ID + SZ_SPARK_ID,
- MR_TS_EVENT_SPARK_STEAL
- },
- {
- /*
- ** The named context has begun executing on the engine
- ** named by the current block.
- */
- MR_TS_EVENT_RUN_THREAD,
- "Run context",
- SZ_CONTEXT_ID,
- 0xFFFF
- },
- {
- /*
- ** The named context has stopped executing on the engine named by
- ** the current block. The reason why the context stopped is given.
- */
- MR_TS_EVENT_STOP_THREAD,
- "Context stopped",
- SZ_CONTEXT_ID + SZ_CONTEXT_STOP_REASON,
- 0xFFFF
- },
- {
- /*
- ** This event is posted when a context is created for a spark.
- */
- MR_TS_EVENT_CREATE_SPARK_THREAD,
- "Create a context for executing a spark",
- SZ_CONTEXT_ID,
- 0xFFFF
- },
- {
- MR_TS_EVENT_LOG_MSG,
- "A user-provided log message",
- -1, /* Variable length */
- 0xFFFF
- },
- {
- /*
- ** Start a garbage collection run.
- */
- MR_TS_EVENT_GC_START,
- "Start GC",
- 0,
- 0xFFFF
- },
- {
- /*
- ** Stop a garbage collection run.
- */
- MR_TS_EVENT_GC_END,
- "Stop GC",
- 0,
- 0xFFFF
- },
- {
- /*
- ** The runtime system registers a string and an ID for it
- ** so that the ID represents the string in future messages.
- */
- MR_TS_EVENT_INTERN_STRING,
- "Register an id->string mapping",
- -1,
- 0xFFFF
- },
- {
- MR_TS_EVENT_CAPSET_CREATE,
- "Create an engine set",
- SZ_CAPSET_ID + SZ_CAPSET_TYPE,
- 0xFFFF
- },
- {
- MR_TS_EVENT_CAPSET_DELETE,
- "Detete an engine set",
- SZ_CAPSET_ID,
- 0xFFFF
- },
- {
- MR_TS_EVENT_CAPSET_ASSIGN_CAP,
- "Add an engine to an engine set",
- SZ_CAPSET_ID + SZ_ENGINE_ID,
- 0xFFFF
- },
- {
- MR_TS_EVENT_CAPSET_REMOVE_CAP,
- "Add an engine to an engine set",
- SZ_CAPSET_ID + SZ_ENGINE_ID,
- 0xFFFF
- },
- {
- MR_TS_EVENT_RTS_IDENTIFIER,
- "The type of the runtime system for this capset",
- -1,
- 0xFFFF
- },
- {
- MR_TS_EVENT_PROGRAM_ARGS,
- "The command line arguments of this process",
- -1,
- 0xFFFF
- },
- {
- MR_TS_EVENT_PROGRAM_ENV,
- "The environment variables this process inherited",
- -1,
- 0xFFFF
- },
- {
- MR_TS_EVENT_OSPROCESS_PID,
- "The pid of this process",
- SZ_PID,
- 0xFFFF
- },
- {
- MR_TS_EVENT_OSPROCESS_PPID,
- "The parent pid of this process",
- SZ_PID,
- 0xFFFF
- },
- {
- MR_TS_EVENT_SPARK_CREATE,
- "A spark is being created",
- 0,
- 0xFFFF
- },
- /*
- * We don't use events 43--53.
- */
- {
- MR_TS_EVENT_GC_GLOBAL_SYNC,
- /*
- ** If using parallel marking this also means that marker threads are
- ** ready. This doesn't apply to Mercury as Boehm uses seperate
- ** threads
- */
- "The world has stopped and GC may begin",
- 0,
- 0xFFFF
- },
- {
- MR_TS_MER_EVENT_SPARK_CREATE,
- "A spark is being created with attributes for Mercury",
- SZ_DYN_CONJ_ID + SZ_SPARK_ID,
- MR_TS_EVENT_SPARK_CREATE
- },
- {
- MR_TS_MER_EVENT_START_PAR_CONJ,
- "Start a parallel conjunction (dyn id, static id)",
- SZ_DYN_CONJ_ID + SZ_STATIC_CONJ_ID,
- 0xFFFF
- },
- {
- MR_TS_MER_EVENT_END_PAR_CONJ,
- "End a parallel conjunction (dyn id)",
- SZ_DYN_CONJ_ID,
- 0xFFFF
- },
- {
- MR_TS_MER_EVENT_END_PAR_CONJUNCT,
- "End a parallel conjunct (dyn id)",
- SZ_DYN_CONJ_ID,
- 0xFFFF
- },
- {
- /*
- ** The dynamic conjunction id can be inferred from context;
- ** it is the next conjunction started after this event.
- */
- MR_TS_MER_EVENT_FUT_CREATE,
- "Create a future (future id)",
- SZ_FUTURE_ID + SZ_VAR_NAME_ID,
- 0xFFFF
- },
- {
- MR_TS_MER_EVENT_FUT_WAIT_NOSUSPEND,
- "Wait on a future without suspending (future id)",
- SZ_FUTURE_ID,
- 0xFFFF
- },
- {
- MR_TS_MER_EVENT_FUT_WAIT_SUSPENDED,
- "Wait on a future by suspending this thread (future id)",
- SZ_FUTURE_ID,
- 0xFFFF
- },
- {
- MR_TS_MER_EVENT_FUT_SIGNAL,
- "Signal a future (future id)",
- SZ_FUTURE_ID,
- 0xFFFF
- },
- {
- MR_TS_MER_EVENT_LOOKING_FOR_GLOBAL_CONTEXT,
- "Engine begins looking for a context to execute",
- 0,
- 0xFFFF
- },
- {
- MR_TS_MER_EVENT_LOOKING_FOR_LOCAL_SPARK,
- "Engine begins looking for a local spark to execute",
- 0,
- 0xFFFF
- },
- {
- MR_TS_MER_EVENT_WORK_STEALING,
- "Engine begins attempt to steal work",
- 0,
- 0xFFFF
- },
- {
- MR_TS_MER_EVENT_RELEASE_CONTEXT,
- "Release this context to the free context pool",
- SZ_CONTEXT_ID,
- 0xFFFF
- },
- {
- MR_TS_MER_EVENT_ENGINE_SLEEPING,
- "This engine is going to sleep",
- 0,
- 0xFFFF
- },
- {
- /*
- ** The runtime system is about to call main/2.
- ** This message has no parameters.
- */
- MR_TS_MER_EVENT_CALLING_MAIN,
- "About to call main/2",
- 0,
- 0xFFFF
- },
- {
- /*
- ** The runtime system is re-useing a previous context and
- ** re-assigning its ID.
- */
- MR_TS_MER_EVENT_REUSE_THREAD,
- "Reusing a previously allocated thread",
- SZ_CONTEXT_ID + SZ_CONTEXT_ID,
- MR_TS_EVENT_CREATE_THREAD
- },
- {
- /* Mark the end of this array. */
- MR_TS_NUM_EVENT_TAGS,
- NULL,
- 0,
- 0xFFFF
- }
-};
-
-/*
-** These tables are filled in when the header of the log file is written.
-** While they can be inferred from the event_type_desc structure,
-** they allow for constant time lookup.
-*/
-static MR_int_least16_t event_type_sizes[MR_TS_NUM_EVENT_TAGS];
-static MR_int_least16_t event_type_sizes_mercury[MR_TS_NUM_MER_EVENTS];
-
-static FILE* MR_threadscope_output_file = NULL;
-static char* MR_threadscope_output_filename;
-
-/*
-** The TSC value recorded when the primordial thread called
-** MR_setup_threadscope(), this is used retroactivly to initialise the
-** MR_eng_cpu_clock_ticks_offset field in the engine structure once it is
-** created.
-*/
-static MR_uint_least64_t MR_primordial_first_tsc;
-
-static Timedelta MR_global_offset;
-
-static struct MR_threadscope_event_buffer global_buffer;
-
-/*
-** Alternativly we use gettimeofday for measuring time.
-*/
-MR_bool MR_threadscope_use_tsc = MR_FALSE;
-static Timedelta MR_gettimeofday_offset;
-
-/*
-** An ID that may be allocated to the next string to be registered.
-*/
-static MR_TS_StringId MR_next_string_id = 0;
-static MR_EngSetId next_engset_id = 0;
-
-static MR_EngSetId process_engset_id;
-
-/***************************************************************************/
-
-static MR_EngSetId
-get_next_engset_id(void)
-{
- /*
- ** This is a seperate function as I may have to add locking or atomic ops
- ** later.
- */
- return next_engset_id++;
-}
-
-/***************************************************************************/
-
-MR_STATIC_INLINE MR_int_least16_t
-event_type_size(EventType event_type) {
- MR_int_least16_t size;
-
- if (event_type < MR_TS_NUM_EVENT_TAGS) {
- size = event_type_sizes[event_type];
- } else if ((event_type < (MR_TS_MER_EVENT_START + MR_TS_NUM_MER_EVENTS))
- && (event_type >= MR_TS_MER_EVENT_START)) {
- size = event_type_sizes_mercury[event_type - MR_TS_MER_EVENT_START];
- } else {
- fprintf(stderr, "Unknown event type %d\n", event_type);
- abort();
- }
-
- return size;
-}
-
-/*
-** Is there enough room in the current engine's buffer
-** for this statically sized event _and_ for the block marker event.
-*/
-MR_STATIC_INLINE MR_bool
-enough_room_for_event(struct MR_threadscope_event_buffer *buffer,
- EventType event_type)
-{
- int needed =
- buffer->MR_tsbuffer_pos +
- event_type_size(event_type) +
- event_type_size(MR_TS_EVENT_BLOCK_MARKER) +
- ((2 + 8) * 2); /* (EventType, Time) * 2 */
- return needed < MR_TS_BUFFERSIZE;
-}
-
-MR_STATIC_INLINE MR_bool
-enough_room_for_variable_size_event(struct MR_threadscope_event_buffer *buffer,
- MR_Unsigned length)
-{
- int needed =
- buffer->MR_tsbuffer_pos +
- length +
- event_type_size(MR_TS_EVENT_BLOCK_MARKER) +
- ((2 + 8) * 2); /* (EventType, Time) * 2 */
- return needed < MR_TS_BUFFERSIZE;
-}
-
-/*
-** Is a block currently open?
-*/
-MR_STATIC_INLINE MR_bool block_is_open(
- struct MR_threadscope_event_buffer *buffer)
-{
- return !(buffer->MR_tsbuffer_block_open_pos == -1);
-}
-
-/*
-** Put words into the current engine's buffer in big endian order.
-*/
-MR_STATIC_INLINE void put_byte(struct MR_threadscope_event_buffer *buffer,
- int byte)
-{
- buffer->MR_tsbuffer_data[buffer->MR_tsbuffer_pos++] = byte;
-}
-
-MR_STATIC_INLINE void put_be_int16(struct MR_threadscope_event_buffer *buffer,
- MR_int_least16_t word)
-{
- put_byte(buffer, (word >> 8) & 0xFF);
- put_byte(buffer, word & 0xFF);
-}
-
-MR_STATIC_INLINE void put_be_uint16(struct MR_threadscope_event_buffer *buffer,
- MR_uint_least16_t word)
-{
- put_byte(buffer, (word >> 8) & 0xFF);
- put_byte(buffer, word & 0xFF);
-}
-
-MR_STATIC_INLINE void put_be_uint32(struct MR_threadscope_event_buffer *buffer,
- MR_uint_least32_t word)
-{
- put_be_uint16(buffer, (word >> 16) & 0xFFFF);
- put_be_uint16(buffer, word & 0xFFFF);
-}
-
-MR_STATIC_INLINE void put_be_uint64(struct MR_threadscope_event_buffer *buffer,
- MR_uint_least64_t word)
-{
- put_be_uint32(buffer, (word >> 32) & 0xFFFFFFFF);
- put_be_uint32(buffer, word & 0xFFFFFFFF);
-}
-
-MR_STATIC_INLINE void put_raw_string(
- struct MR_threadscope_event_buffer *buffer,
- const char *string, unsigned len)
-{
- unsigned i;
- for (i = 0; i < len; i++) {
- put_byte(buffer, string[i]);
- }
-}
-
-/*
-** Put a string in the given buffer. The string will be preceeded
-** by a 16 bit integer giving the string's length.
-*/
-MR_STATIC_INLINE void put_string_size16(
- struct MR_threadscope_event_buffer *buffer, const char *string)
-{
- unsigned i, len;
-
- len = strlen(string);
- put_be_uint16(buffer, len);
- put_raw_string(buffer, string, len);
-}
-
-/*
-** Put a string in the given buffer. The string will be preceeded
-** by a 32 bit integer giving the string's length.
-*/
-MR_STATIC_INLINE void put_string_size32(
- struct MR_threadscope_event_buffer *buffer, const char *string)
-{
- unsigned i, len;
-
- len = strlen(string);
- put_be_uint32(buffer, len);
- put_raw_string(buffer, string, len);
-}
-
-MR_STATIC_INLINE void put_timestamp(struct MR_threadscope_event_buffer *buffer,
- Time timestamp)
-{
- put_be_uint64(buffer, timestamp);
-}
-
-MR_STATIC_INLINE void put_eventlog_offset(
- struct MR_threadscope_event_buffer *buffer, EventlogOffset offset)
-{
- put_be_uint32(buffer, offset);
-}
-
-MR_STATIC_INLINE void put_event_header(
- struct MR_threadscope_event_buffer *buffer,
- EventType event_type, Time timestamp)
-{
- put_be_uint16(buffer, event_type);
- put_timestamp(buffer, timestamp);
-}
-
-MR_STATIC_INLINE void put_engine_id(struct MR_threadscope_event_buffer *buffer,
- MR_EngineId engine_num)
-{
- put_be_uint16(buffer, engine_num);
-}
-
-MR_STATIC_INLINE void put_context_id(
- struct MR_threadscope_event_buffer *buffer, MR_ContextId context_id)
-{
- put_be_uint32(buffer, context_id);
-}
-
-MR_STATIC_INLINE void put_stop_reason(
- struct MR_threadscope_event_buffer *buffer, MR_ContextStopReason reason)
-{
- put_be_uint16(buffer, reason);
-}
-
-MR_STATIC_INLINE void put_string_id(struct MR_threadscope_event_buffer *buffer,
- MR_TS_StringId id)
-{
- put_be_uint32(buffer, id);
-}
-
-MR_STATIC_INLINE void put_par_conj_dynamic_id(
- struct MR_threadscope_event_buffer *buffer, MR_Word* id)
-{
- put_be_uint64(buffer, (MR_Word)id);
-}
-
-MR_STATIC_INLINE void put_spark_id(struct MR_threadscope_event_buffer *buffer,
- MR_SparkId spark_id)
-{
- put_be_uint32(buffer, spark_id);
-}
-
-MR_STATIC_INLINE void put_engset_id(struct MR_threadscope_event_buffer *buffer,
- MR_EngSetId engset_id)
-{
- put_be_uint32(buffer, engset_id);
-}
-
-MR_STATIC_INLINE void put_engset_type(
- struct MR_threadscope_event_buffer *buffer, MR_EngSetType type)
-{
- put_be_uint16(buffer, type);
-}
-
-MR_STATIC_INLINE void put_future_id(struct MR_threadscope_event_buffer *buffer,
- MR_Future* id)
-{
- put_be_uint64(buffer, (MR_Word)id);
-}
-
-/***************************************************************************/
-
-static struct MR_threadscope_event_buffer* MR_create_event_buffer(void);
-
-/*
-** The prelude is everything up to and including the 'DATA_BEGIN' marker.
-*/
-static void MR_open_output_file_and_write_prelude(void);
-
-static void MR_close_output_file(void);
-
-static void put_event_type(struct MR_threadscope_event_buffer *buffer,
- EventTypeDesc *event_type);
-
-static MR_bool flush_event_buffer(struct MR_threadscope_event_buffer *buffer);
-
-static void maybe_close_block(struct MR_threadscope_event_buffer *buffer);
-
-static void open_block(struct MR_threadscope_event_buffer *buffer,
- MR_Unsigned eng_id);
-
-/***************************************************************************/
-
-static MR_TS_StringId
-MR_threadscope_register_string(const char *string);
-
-/*
-** These four events are used to create and manage engine sets.
-** Haskell calls these cap sets.
-**
-** The first two work on the global event buffer and are not thread safe.
-*/
-static void MR_threadscope_post_create_engset(MR_EngSetId id,
- MR_EngSetType type);
-
-static void MR_threadscope_post_destroy_engset(MR_EngSetId id);
-
-static void MR_threadscope_post_engset_add(
- struct MR_threadscope_event_buffer *buffer,
- MR_EngSetId id, MR_EngineId eng);
-
-static void MR_threadscope_post_engset_remove(MR_EngSetId id, MR_EngineId eng);
-
-/*
-** Post the name and version of the runtime system to the log file.
-**
-** Note that this is the name of the implementation (mmc),
-** not the name of the language (mercury).
-**
-** The name and version are separated by a '-'.
-*/
-static void MR_threadscope_post_runtime_identifier(MR_EngSetId id,
- const char *ident);
-
-/***************************************************************************/
-
-static void start_gc_callback(void);
-static void stop_gc_callback(void);
-static void pause_thread_gc_callback(void);
-static void resume_thread_gc_callback(void);
-
-/***************************************************************************/
-
-static Time get_current_time_nanosecs(void);
-static Time gettimeofday_nsecs(void);
-
-/***************************************************************************/
-
-void
-MR_setup_threadscope(void)
-{
- MR_DO_THREADSCOPE_DEBUG(
- fprintf(stderr, "In setup threadscope thread: 0x%lx\n", pthread_self())
- );
-
- if (!MR_tsc_is_sensible()) {
- MR_threadscope_use_tsc = MR_FALSE;
- }
-
- if (MR_threadscope_use_tsc) {
- /* This value is used later when setting up the primordial engine. */
- MR_primordial_first_tsc = MR_read_cpu_tsc();
-
- /*
- ** These variables are used for TSC synchronization which is not used.
- ** See below.
- **
- pthread_mutex_init(&MR_tsc_sync_slave_lock, MR_MUTEX_ATTR);
- MR_US_COND_CLEAR(&MR_tsc_sync_slave_entry_cond);
- MR_US_COND_CLEAR(&MR_tsc_sync_master_entry_cond);
- MR_US_COND_CLEAR(&MR_tsc_sync_t0);
- MR_US_COND_CLEAR(&MR_tsc_sync_t1);
- */
-
- } else {
- MR_gettimeofday_offset = -1 * gettimeofday_nsecs();
- }
-
- /* Configure Boehm */
-#ifdef MR_BOEHM_GC
- GC_mercury_callback_start_collect = start_gc_callback;
- GC_mercury_callback_stop_collect = stop_gc_callback;
- GC_mercury_callback_pause_thread = pause_thread_gc_callback;
- GC_mercury_callback_resume_thread = resume_thread_gc_callback;
-#endif
-
- /* Clear the global buffer and setup the file */
- global_buffer.MR_tsbuffer_pos = 0;
- global_buffer.MR_tsbuffer_block_open_pos = -1;
- global_buffer.MR_tsbuffer_lock = MR_US_LOCK_INITIAL_VALUE;
- MR_open_output_file_and_write_prelude();
-
- /*
- ** Post the initial events to the buffer.
- */
- process_engset_id = get_next_engset_id();
- MR_threadscope_post_create_engset(process_engset_id,
- MR_TS_ENGSET_TYPE_OSPROCESS);
- MR_threadscope_post_runtime_identifier(process_engset_id,
- "mmc-" MR_VERSION);
-
- /*
- ** Put the startup event in the buffer.
- */
- put_event_header(&global_buffer, MR_TS_EVENT_STARTUP, 0);
- put_engine_id(&global_buffer, (MR_EngineId)MR_num_threads);
-
- flush_event_buffer(&global_buffer);
-}
-
-void
-MR_finalize_threadscope(void)
-{
- MR_DO_THREADSCOPE_DEBUG(
- fprintf(stderr, "In finalize threadscope thread: 0x%lx\n",
- pthread_self())
- );
-
- MR_threadscope_post_destroy_engset(process_engset_id);
-
- flush_event_buffer(&global_buffer);
- MR_close_output_file();
-}
-
-void
-MR_threadscope_setup_engine(MercuryEngine *eng)
-{
- MR_DO_THREADSCOPE_DEBUG(
- fprintf(stderr, "In threadscope setup engine thread: 0x%lx\n",
- pthread_self())
- );
- eng->MR_eng_next_spark_id = 0;
-
- if (MR_threadscope_use_tsc) {
- if (eng->MR_eng_id == 0) {
- MR_global_offset = -MR_primordial_first_tsc;
- }
- eng->MR_eng_cpu_clock_ticks_offset = MR_global_offset;
- }
-
- eng->MR_eng_ts_buffer = MR_create_event_buffer();
-
- MR_threadscope_post_engset_add(eng->MR_eng_ts_buffer, process_engset_id,
- eng->MR_eng_id);
- /*
- ** Flush the buffer to ensure the message above (which lacks a timestamp)
- ** appears in a sensible place in the buffer.
- */
- flush_event_buffer(eng->MR_eng_ts_buffer);
-}
-
-void
-MR_threadscope_finalize_engine(MercuryEngine *eng)
-{
- struct MR_threadscope_event_buffer *buffer = eng->MR_eng_ts_buffer;
-
- MR_DO_THREADSCOPE_DEBUG(
- fprintf(stderr, "In threadscope finalize engine thread: 0x%lx\n",
- pthread_self())
- );
-
- MR_threadscope_post_engset_remove(process_engset_id, eng->MR_eng_id);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
-
- if (!enough_room_for_event(buffer, MR_TS_EVENT_SHUTDOWN)) {
- flush_event_buffer(buffer);
- open_block(buffer, eng->MR_eng_id);
- } else if (!block_is_open(buffer)) {
- open_block(buffer, eng->MR_eng_id);
- }
- put_event_header(buffer, MR_TS_EVENT_SHUTDOWN, get_current_time_nanosecs());
-
- flush_event_buffer(buffer);
- eng->MR_eng_ts_buffer = NULL;
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-#if 0
-/*
-** It looks like we don't need this on modern CPUs including multi-socket
-** systems (goliath). If we find systems where this is needed, we can
-** enable it via a runtime check.
-*/
-/*
-** The synchronization of TSCs operates as follows:
-** The master and slave enter their functions. Both threads spin until the
-** other is ready (signaling the other before they begin spinning). Then for
-** MR_TSC_SYNC_NUM_ROUNDS: The master spins waiting for the slave. The slave
-** records it's current TSC, signals the master and spins waiting for a reply.
-** The master upon hearing from the slave records it's TSC and then signals
-** the slave. The slave can then compute the delay in this round. The slave
-** takes the NR_TSC_SYNC_NUM_BEST_ROUNDS best delays (smallest) and computes
-** the offset as the average between between the difference of the clocks based
-** on Cristan's algorithm (1989).
-*/
-
-typedef struct {
- Timedelta delay;
- Timedelta offset;
-} TimeDelayOffset;
-
-static MercuryLock MR_tsc_sync_slave_lock;
-volatile static MR_Us_Cond MR_tsc_sync_slave_entry_cond;
-volatile static MR_Us_Cond MR_tsc_sync_master_entry_cond;
-volatile static MR_Us_Cond MR_tsc_sync_t0;
-volatile static MR_Us_Cond MR_tsc_sync_t1;
-static Time MR_tsc_sync_master_time;
-
-static int compare_time_delay_offset_by_delay(const void *a, const void *b);
-
-void
-MR_threadscope_sync_tsc_master(void)
-{
- unsigned i;
-
- /* Wait for a slave to enter. */
- MR_US_COND_SET(&MR_tsc_sync_master_entry_cond);
- MR_US_SPIN_COND(&MR_tsc_sync_slave_entry_cond);
- MR_US_COND_CLEAR(&MR_tsc_sync_slave_entry_cond);
-
- for (i = 0; i < MR_TSC_SYNC_NUM_ROUNDS; i++) {
- /* Wait to receive the message from the slave at T0. */
- MR_US_SPIN_COND(&MR_tsc_sync_t0);
- MR_US_COND_CLEAR(&MR_tsc_sync_t0);
-
- /* Read our TSC and send the slave a message. */
- MR_tsc_sync_master_time = MR_read_cpu_tsc();
- MR_US_COND_SET(&MR_tsc_sync_t1);
- }
-
-}
-
-void
-MR_threadscope_sync_tsc_slave(void)
-{
- unsigned i, j;
- TimeDelayOffset delay_offset[MR_TSC_SYNC_NUM_ROUNDS];
- Timedelta total_offset;
- MercuryEngine *eng = MR_thread_engine_base;
-
- /* Only one slave may enter at a time. */
- MR_LOCK(&MR_tsc_sync_slave_lock, "MR_threadscope_sync_tsc_slave");
-
- /*
- ** Tell the master we are ready to begin, and wait for it to tell us
- ** it is ready.
- */
- MR_US_COND_SET(&MR_tsc_sync_slave_entry_cond);
- MR_US_SPIN_COND(&MR_tsc_sync_master_entry_cond);
- MR_US_COND_CLEAR(&MR_tsc_sync_master_entry_cond);
-
- for (i = 0; i < MR_TSC_SYNC_NUM_ROUNDS; i++) {
- Time slave_tsc_at_t0;
- Time slave_tsc_at_t2;
-
- /*
- ** Get the current time and signal that we've done so (T=0).
- */
- slave_tsc_at_t0 = MR_read_cpu_tsc();
- MR_US_COND_SET(&MR_tsc_sync_t0);
-
- /*
- ** Wait for the master to reply, the master handles T=1,
- ** here we proceed to T=2.
- */
- MR_US_SPIN_COND(&MR_tsc_sync_t1);
- slave_tsc_at_t2 = MR_read_cpu_tsc();
- MR_US_COND_CLEAR(&MR_tsc_sync_t1);
-
- /*
- ** Here are Cristian's formulas. Delay is the round trip time,
- ** slave_tsc_at_t0 + delay/2 is the time on the slave's clock that the
- ** master processed the slaves message and sent its own. This is
- ** accurate if the communication delays in either direction are
- ** uniform, that is communication latency is synchronous.
- */
- delay_offset[i].delay = slave_tsc_at_t2 - slave_tsc_at_t0;
- delay_offset[i].offset = MR_tsc_sync_master_time -
- (slave_tsc_at_t0 + delay_offset[i].delay/2);
- }
-
- /*
- ** By now the master thread will return,
- ** and continue with its normal work.
- */
-
- /*
- ** We do this debugging output while holding the lock, so that the output
- ** is reasonable.
- */
- MR_DO_THREADSCOPE_DEBUG({
- fprintf(stderr, "TSC Synchronization for thread 0x%x\n",
- pthread_self());
- for (i = 0; i < MR_TSC_SYNC_NUM_ROUNDS; i++) {
- fprintf(stderr,
- "delay: %ld offset (local + global = total) %ld + %ld = %ld\n",
- delay_offset[i].delay, delay_offset[i].offset, MR_global_offset,
- delay_offset[i].offset + MR_global_offset);
- }
- });
- MR_UNLOCK(&MR_tsc_sync_slave_lock, "MR_threadscope_sync_tsc_slave");
-
- /* Now to average the best offsets. */
- qsort(&delay_offset, MR_TSC_SYNC_NUM_ROUNDS, sizeof(TimeDelayOffset),
- compare_time_delay_offset_by_delay);
- total_offset = 0;
- for (i = 0; i < MR_TSC_SYNC_NUM_BEST_ROUNDS; i++) {
- total_offset = delay_offset[i].offset;
- }
- eng->MR_eng_cpu_clock_ticks_offset = total_offset + MR_global_offset;
-
- MR_DO_THREADSCOPE_DEBUG({
- fprintf(stderr, "TSC Synchronization offset for thread 0x%x: %ld\n",
- pthread_self(), eng->MR_eng_cpu_clock_ticks_offset);
- });
-}
-
-static int
-compare_time_delay_offset_by_delay(const void *a, const void *b) {
- TimeDelayOffset *tdo_a = (TimeDelayOffset*)a;
- TimeDelayOffset *tdo_b = (TimeDelayOffset*)b;
-
- if (tdo_a->delay > tdo_b->delay) {
- return 1;
- } else if (tdo_a->delay < tdo_b->delay) {
- return -1;
- } else {
- return 0;
- }
-}
-
-#endif
-
-/***************************************************************************/
-
-void
-MR_threadscope_post_create_context(MR_Context *context)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
-
- if (!enough_room_for_event(buffer, MR_TS_EVENT_CREATE_THREAD)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_EVENT_CREATE_THREAD,
- get_current_time_nanosecs());
- put_context_id(buffer, context->MR_ctxt_num_id);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_reuse_context(MR_Context *context, MR_Unsigned old_id)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
-
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_REUSE_THREAD)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_REUSE_THREAD,
- get_current_time_nanosecs());
- put_context_id(buffer, context->MR_ctxt_num_id);
- put_context_id(buffer, old_id);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_create_context_for_spark(MR_Context *context)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
-
- if (!enough_room_for_event(buffer, MR_TS_EVENT_CREATE_SPARK_THREAD)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_EVENT_CREATE_SPARK_THREAD,
- get_current_time_nanosecs());
- put_context_id(buffer, context->MR_ctxt_num_id);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_release_context(MR_Context *context)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
-
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_RELEASE_CONTEXT)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_RELEASE_CONTEXT,
- get_current_time_nanosecs());
- put_context_id(buffer, context->MR_ctxt_num_id);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_context_runnable(MR_Context *context)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
-
- if (!enough_room_for_event(buffer, MR_TS_EVENT_THREAD_RUNNABLE)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_EVENT_THREAD_RUNNABLE,
- get_current_time_nanosecs());
- put_context_id(buffer, context->MR_ctxt_num_id);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-static void
-MR_threadscope_post_run_context_locked(
- struct MR_threadscope_event_buffer *buffer, MR_Context *context)
-{
- if (!enough_room_for_event(buffer, MR_TS_EVENT_RUN_THREAD)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_thread_engine_base->MR_eng_id);
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_thread_engine_base->MR_eng_id);
- }
-
- put_event_header(buffer, MR_TS_EVENT_RUN_THREAD,
- get_current_time_nanosecs());
- put_context_id(buffer,
- MR_thread_engine_base->MR_eng_this_context->MR_ctxt_num_id);
-}
-
-void
-MR_threadscope_post_run_context(void)
-{
- struct MR_threadscope_event_buffer *buffer;
- MR_Context *context;
-
- buffer = MR_thread_engine_base->MR_eng_ts_buffer;
-
- context = MR_thread_engine_base->MR_eng_this_context;
-
- if (context) {
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (buffer->MR_tsbuffer_ctxt_is_stopped) {
- MR_threadscope_post_run_context_locked(buffer, context);
- buffer->MR_tsbuffer_ctxt_is_stopped = MR_FALSE;
- }
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
- }
-}
-
-static void
-MR_threadscope_post_stop_context_locked(
- struct MR_threadscope_event_buffer *buffer,
- MR_Context *context, MR_ContextStopReason reason)
-{
- if (!enough_room_for_event(buffer, MR_TS_EVENT_STOP_THREAD)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_thread_engine_base->MR_eng_id);
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_thread_engine_base->MR_eng_id);
- }
-
- put_event_header(buffer, MR_TS_EVENT_STOP_THREAD,
- get_current_time_nanosecs());
- put_context_id(buffer, context->MR_ctxt_num_id);
- put_stop_reason(buffer, reason);
-}
-
-void
-MR_threadscope_post_stop_context(MR_ContextStopReason reason)
-{
- struct MR_threadscope_event_buffer *buffer;
- MR_Context *context;
-
- buffer = MR_thread_engine_base->MR_eng_ts_buffer;
- context = MR_thread_engine_base->MR_eng_this_context;
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!buffer->MR_tsbuffer_ctxt_is_stopped) {
- MR_threadscope_post_stop_context_locked(buffer, context, reason);
- buffer->MR_tsbuffer_ctxt_is_stopped = MR_TRUE;
- }
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_run_spark(MR_SparkId spark_id)
-{
- struct MR_threadscope_event_buffer *buffer;
-
- buffer = MR_thread_engine_base->MR_eng_ts_buffer;
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_SPARK_RUN)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_SPARK_RUN,
- get_current_time_nanosecs());
- put_spark_id(buffer, spark_id);
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_steal_spark(MR_SparkId spark_id)
-{
- struct MR_threadscope_event_buffer *buffer;
- unsigned engine_id;
-
- buffer = MR_thread_engine_base->MR_eng_ts_buffer;
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_SPARK_STEAL)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_SPARK_STEAL,
- get_current_time_nanosecs());
-
- /*
- ** The engine that created the spark (which may not be whom it was stolen
- ** from if different work-stealking algorithms are implemented) can be
- ** derived from the spark id.
- */
- engine_id = (spark_id & 0xFF000000) >> 24;
- put_be_uint16(buffer, engine_id);
- put_spark_id(buffer, spark_id);
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_sparking(MR_Word* dynamic_conj_id, MR_SparkId spark_id)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_SPARK_CREATE)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_SPARK_CREATE,
- get_current_time_nanosecs());
- put_par_conj_dynamic_id(buffer, dynamic_conj_id);
- put_spark_id(buffer, spark_id);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_calling_main(void)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_CALLING_MAIN)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_CALLING_MAIN,
- get_current_time_nanosecs());
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_looking_for_global_context(void)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer,
- MR_TS_MER_EVENT_LOOKING_FOR_GLOBAL_CONTEXT))
- {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_LOOKING_FOR_GLOBAL_CONTEXT,
- get_current_time_nanosecs());
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_looking_for_local_spark(void)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_LOOKING_FOR_LOCAL_SPARK)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_LOOKING_FOR_LOCAL_SPARK,
- get_current_time_nanosecs());
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_work_stealing(void)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_WORK_STEALING)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_WORK_STEALING,
- get_current_time_nanosecs());
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_start_par_conj(MR_Word* dynamic_id,
- MR_TS_StringId static_id)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_START_PAR_CONJ)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_START_PAR_CONJ,
- get_current_time_nanosecs());
- put_par_conj_dynamic_id(buffer, dynamic_id);
- put_string_id(buffer, static_id);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_end_par_conj(MR_Word *dynamic_id)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_END_PAR_CONJ)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_END_PAR_CONJ,
- get_current_time_nanosecs());
- put_par_conj_dynamic_id(buffer, dynamic_id);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_end_par_conjunct(MR_Word *dynamic_id)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_END_PAR_CONJUNCT)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_END_PAR_CONJUNCT,
- get_current_time_nanosecs());
- put_par_conj_dynamic_id(buffer, dynamic_id);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-/*
-** Register a string for use in future messages.
-*/
-static MR_TS_StringId
-MR_threadscope_register_string(const char *string)
-{
- MR_TS_StringId id;
- unsigned length;
-
- length = strlen(string);
-
- /*
- ** +2 for the event length.
- ** +4 for the string id.
- */
- if (!enough_room_for_variable_size_event(&global_buffer, strlen(string)
- + 2 + 4))
- {
- flush_event_buffer(&global_buffer);
- }
-
- put_event_header(&global_buffer, MR_TS_EVENT_INTERN_STRING, 0);
- id = MR_next_string_id++;
- put_be_uint16(&global_buffer, length + 4);
- put_raw_string(&global_buffer, string, length);
- put_string_id(&global_buffer, id);
-
- return id;
-}
-
-void
-MR_threadscope_register_strings_array(MR_Threadscope_String *array,
- unsigned size)
-{
- unsigned i;
-
- for (i = 0; i < size; i++) {
- array[i].MR_tsstring_id =
- MR_threadscope_register_string(array[i].MR_tsstring_string);
- }
-
- flush_event_buffer(&global_buffer);
-}
-
-void
-MR_threadscope_post_log_msg(const char *message)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_variable_size_event(buffer, strlen(message) + 2)) {
- flush_event_buffer(buffer),
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_EVENT_LOG_MSG,
- get_current_time_nanosecs());
- put_string_size16(buffer, message);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_create_engset(MR_EngSetId id, MR_EngSetType type)
-{
- struct MR_threadscope_event_buffer *buffer = &global_buffer;
-
- if (!enough_room_for_event(buffer, MR_TS_EVENT_CAPSET_CREATE)) {
- flush_event_buffer(buffer);
- }
-
- put_event_header(buffer, MR_TS_EVENT_CAPSET_CREATE, 0);
- put_engset_id(buffer, id);
- put_engset_type(buffer, type);
-}
-
-void
-MR_threadscope_post_destroy_engset(MR_EngSetId id)
-{
- struct MR_threadscope_event_buffer *buffer = &global_buffer;
-
- if (!enough_room_for_event(buffer, MR_TS_EVENT_CAPSET_DELETE)) {
- flush_event_buffer(buffer);
- }
-
- put_event_header(buffer, MR_TS_EVENT_CAPSET_DELETE,
- get_current_time_nanosecs());
- put_engset_id(buffer, id);
-}
-
-void
-MR_threadscope_post_engset_add(struct MR_threadscope_event_buffer *buffer,
- MR_EngSetId id, MR_EngineId eng)
-{
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- maybe_close_block(buffer);
- if (!enough_room_for_event(buffer, MR_TS_EVENT_CAPSET_ASSIGN_CAP)) {
- flush_event_buffer(buffer);
- }
-
- /*
- ** When this event occurs the engine hasn't been setup yet. Even though
- ** we use the engine's buffer, we cannot use get_current_time_nanosecs().
- */
- put_event_header(buffer, MR_TS_EVENT_CAPSET_ASSIGN_CAP, 0);
- put_engset_id(buffer, id);
- put_engine_id(buffer, eng);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_engset_remove(MR_EngSetId id, MR_EngineId eng)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- maybe_close_block(buffer);
- if (!enough_room_for_event(buffer, MR_TS_EVENT_CAPSET_REMOVE_CAP)) {
- flush_event_buffer(buffer);
- }
-
- put_event_header(buffer, MR_TS_EVENT_CAPSET_REMOVE_CAP,
- get_current_time_nanosecs());
- put_engset_id(buffer, id);
- put_engine_id(buffer, eng);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_runtime_identifier(MR_EngSetId engset_id,
- const char *identifier)
-{
- struct MR_threadscope_event_buffer *buffer = &global_buffer;
- unsigned len;
-
- len = strlen(identifier);
-
- if (!enough_room_for_variable_size_event(buffer, len + SZ_CAPSET_ID)) {
- flush_event_buffer(buffer);
- }
-
- put_event_header(buffer, MR_TS_EVENT_RTS_IDENTIFIER, 0);
- put_be_int16(buffer, len + SZ_CAPSET_ID);
- put_engset_id(buffer, engset_id);
- put_raw_string(buffer, identifier, len);
-}
-
-void
-MR_threadscope_post_new_future(MR_Future *future_id, MR_TS_StringId name)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_FUT_CREATE)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_FUT_CREATE,
- get_current_time_nanosecs());
- put_future_id(buffer, future_id);
- put_string_id(buffer, name);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_wait_future_nosuspend(MR_Future* future_id)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_FUT_WAIT_NOSUSPEND)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_FUT_WAIT_NOSUSPEND,
- get_current_time_nanosecs());
- put_future_id(buffer, future_id);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_wait_future_suspended(MR_Future* future_id)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_FUT_WAIT_SUSPENDED)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_FUT_WAIT_SUSPENDED,
- get_current_time_nanosecs());
- put_future_id(buffer, future_id);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void
-MR_threadscope_post_signal_future(MR_Future* future_id)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_FUT_SIGNAL)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_FUT_SIGNAL,
- get_current_time_nanosecs());
- put_future_id(buffer, future_id);
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-void MR_threadscope_post_engine_sleeping(void)
-{
- struct MR_threadscope_event_buffer *buffer = MR_ENGINE(MR_eng_ts_buffer);
-
- MR_US_SPIN_LOCK(&(buffer->MR_tsbuffer_lock));
- if (!enough_room_for_event(buffer, MR_TS_MER_EVENT_ENGINE_SLEEPING)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_ENGINE(MR_eng_id));
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_ENGINE(MR_eng_id));
- }
-
- put_event_header(buffer, MR_TS_MER_EVENT_ENGINE_SLEEPING,
- get_current_time_nanosecs());
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
-}
-
-/***************************************************************************/
-
-static struct MR_threadscope_event_buffer*
-MR_create_event_buffer(void)
-{
- struct MR_threadscope_event_buffer* buffer;
-
- buffer = MR_GC_NEW(MR_threadscope_event_buffer_t);
- buffer->MR_tsbuffer_pos = 0;
- buffer->MR_tsbuffer_block_open_pos = -1;
- buffer->MR_tsbuffer_ctxt_is_stopped = MR_TRUE;
- buffer->MR_tsbuffer_lock = MR_US_LOCK_INITIAL_VALUE;
-
- return buffer;
-}
-
-/***************************************************************************/
-
-static void
-MR_open_output_file_and_write_prelude(void)
-{
- MR_Unsigned filename_len;
- char *progname_copy;
- char *progname_base;
- MR_Unsigned i;
-
- progname_copy = strdup(MR_progname);
- progname_base = basename(progname_copy);
-
- /*
- ** This is an over-approximation on the amount of space needed
- ** for this filename.
- */
- filename_len = strlen(progname_base) + strlen(MR_TS_FILENAME_FORMAT) + 1;
- MR_threadscope_output_filename = MR_GC_NEW_ARRAY(char, filename_len);
- snprintf(MR_threadscope_output_filename, filename_len,
- MR_TS_FILENAME_FORMAT, progname_base);
- free(progname_copy);
- progname_copy = NULL;
- progname_base = NULL;
-
- MR_threadscope_output_file = fopen(MR_threadscope_output_filename, "w");
- if (!MR_threadscope_output_file) {
- perror(MR_threadscope_output_filename);
- return;
- }
-
- put_be_uint32(&global_buffer, MR_TS_EVENT_HEADER_BEGIN);
- put_be_uint32(&global_buffer, MR_TS_EVENT_HET_BEGIN);
- for (i = 0;
- event_type_descs[i].etd_event_type != MR_TS_NUM_EVENT_TAGS;
- i++)
- {
- put_event_type(&global_buffer, &event_type_descs[i]);
- }
- put_be_uint32(&global_buffer, MR_TS_EVENT_HET_END);
- put_be_uint32(&global_buffer, MR_TS_EVENT_HEADER_END);
- put_be_uint32(&global_buffer, MR_TS_EVENT_DATA_BEGIN);
-
- flush_event_buffer(&global_buffer);
-}
-
-static void
-MR_close_output_file(void)
-{
- if (MR_threadscope_output_file) {
- put_be_uint16(&global_buffer, MR_TS_EVENT_DATA_END);
- if (flush_event_buffer(&global_buffer)) {
- if (EOF == fclose(MR_threadscope_output_file)) {
- perror(MR_threadscope_output_filename);
- }
- MR_threadscope_output_file = NULL;
- MR_threadscope_output_filename = NULL;
- }
- }
-}
-
-static void
-put_event_type(struct MR_threadscope_event_buffer *buffer,
- EventTypeDesc *event_type_desc)
-{
- MR_int_least16_t size;
- EventType event_type;
-
- /* This also fills in our tables of event sizes. */
- event_type = event_type_desc->etd_event_type;
- size = event_type_desc->etd_size;
- if (event_type < MR_TS_NUM_EVENT_TAGS) {
- event_type_sizes[event_type] = size;
- } else if ((event_type < (MR_TS_MER_EVENT_START + MR_TS_NUM_MER_EVENTS))
- && (event_type >= MR_TS_MER_EVENT_START))
- {
- event_type_sizes_mercury[event_type - MR_TS_MER_EVENT_START] = size;
- } else {
- fprintf(stderr, "Unknown event type %d\n", event_type);
- abort();
- }
-
- put_be_uint32(buffer, MR_TS_EVENT_ET_BEGIN);
-
- put_be_uint16(buffer, event_type);
- put_be_int16(buffer, size);
-
- put_string_size32(buffer, event_type_desc->etd_description);
-
- if (event_type_desc->edt_extends_event != 0xFFFF) {
- put_be_uint32(buffer, 2 + 2 + SZ_EVENT_TYPE);
- put_be_uint16(buffer, MR_EXT_TYPE_EXTENSION);
- put_be_uint16(buffer, SZ_EVENT_TYPE);
- put_be_uint16(buffer, event_type_desc->edt_extends_event);
- } else {
- /* There is no extended data in this event */
- put_be_uint32(buffer, 0);
- }
-
- put_be_uint32(buffer, MR_TS_EVENT_ET_END);
-}
-
-static MR_bool
-flush_event_buffer(struct MR_threadscope_event_buffer *buffer)
-{
- maybe_close_block(buffer);
-
- /*
- ** fwrite handles locking for us, so we have no concurrent access problems.
- */
- if (MR_threadscope_output_file && buffer->MR_tsbuffer_pos) {
- if (0 == fwrite(buffer->MR_tsbuffer_data, buffer->MR_tsbuffer_pos, 1,
- MR_threadscope_output_file))
- {
- perror(MR_threadscope_output_filename);
- MR_threadscope_output_file = NULL;
- MR_threadscope_output_filename = NULL;
- }
- }
- buffer->MR_tsbuffer_pos = 0;
-
- return (MR_threadscope_output_filename ? MR_TRUE : MR_FALSE);
-}
-
-static void
-maybe_close_block(struct MR_threadscope_event_buffer *buffer)
-{
- MR_Unsigned saved_pos;
-
- if (buffer->MR_tsbuffer_block_open_pos != -1) {
- saved_pos = buffer->MR_tsbuffer_pos;
- buffer->MR_tsbuffer_pos = buffer->MR_tsbuffer_block_open_pos +
- sizeof(EventType) + sizeof(Time);
- put_eventlog_offset(buffer,
- saved_pos - buffer->MR_tsbuffer_block_open_pos);
- put_timestamp(buffer, get_current_time_nanosecs());
-
- buffer->MR_tsbuffer_block_open_pos = -1;
- buffer->MR_tsbuffer_pos = saved_pos;
- }
-}
-
-static void
-open_block(struct MR_threadscope_event_buffer *buffer, MR_Unsigned eng_id)
-{
- maybe_close_block(buffer);
-
- /*
- ** Save the old position. Close block uses this so that it knows
- ** where the block marker is that it should write into.
- */
- buffer->MR_tsbuffer_block_open_pos = buffer->MR_tsbuffer_pos;
-
- put_event_header(buffer, MR_TS_EVENT_BLOCK_MARKER,
- get_current_time_nanosecs());
-
- /* Skip over the next two fields, they are filled in by close_block. */
- buffer->MR_tsbuffer_pos += sizeof(EventlogOffset) + sizeof(Time);
-
- put_engine_id(buffer, eng_id);
-}
-
-static void
-start_gc_callback(void)
-{
- struct MR_threadscope_event_buffer *buffer;
- MR_Context *context;
-
- MR_DO_THREADSCOPE_DEBUG(
- fprintf(stderr, "In gc start callback thread: 0x%lx\n", pthread_self())
- );
- if (MR_thread_engine_base == NULL) {
- return;
- }
- MR_DO_THREADSCOPE_DEBUG(
- fprintf(stderr, "\tEngine: 0x%.16lx\n", MR_thread_engine_base)
- );
- buffer = MR_thread_engine_base->MR_eng_ts_buffer;
- if (buffer == NULL) {
- /* GC might be running before we're done setting up */
- return;
- }
- MR_DO_THREADSCOPE_DEBUG(
- fprintf(stderr, "\tBuffer: 0x%.16lx\n", buffer)
- );
-
- if (MR_US_TRY_LOCK(&(buffer->MR_tsbuffer_lock))) {
- context = MR_thread_engine_base->MR_eng_this_context;
- if (!buffer->MR_tsbuffer_ctxt_is_stopped && context) {
- MR_threadscope_post_stop_context_locked(buffer,
- context, MR_TS_STOP_REASON_HEAP_OVERFLOW);
- }
-
- if (!enough_room_for_event(buffer, MR_TS_EVENT_GC_START)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_thread_engine_base->MR_eng_id);
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_thread_engine_base->MR_eng_id);
- }
-
- put_event_header(buffer, MR_TS_EVENT_GC_START,
- get_current_time_nanosecs());
-
- if (!enough_room_for_event(buffer, MR_TS_EVENT_GC_GLOBAL_SYNC)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_thread_engine_base->MR_eng_id);
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_thread_engine_base->MR_eng_id);
- }
-
- /*
- ** Idealy this event should be posted after the world has stopped.
- ** Doing so means adding more instrumentation into Boehm.
- */
- put_event_header(buffer, MR_TS_EVENT_GC_GLOBAL_SYNC,
- get_current_time_nanosecs());
-
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
- }
-}
-
-static void
-stop_gc_callback(void)
-{
- struct MR_threadscope_event_buffer *buffer;
- MR_Context *context;
-
- MR_DO_THREADSCOPE_DEBUG(
- fprintf(stderr, "In gc stop callback thread: 0x%lx\n", pthread_self());
- );
- if (MR_thread_engine_base == NULL) return;
- buffer = MR_thread_engine_base->MR_eng_ts_buffer;
- if (buffer == NULL) {
- /* GC might be running before we're done setting up */
- return;
- }
-
- if (MR_US_TRY_LOCK(&(buffer->MR_tsbuffer_lock))) {
- if (!enough_room_for_event(buffer, MR_TS_EVENT_GC_END)) {
- flush_event_buffer(buffer);
- open_block(buffer, MR_thread_engine_base->MR_eng_id);
- } else if (!block_is_open(buffer)) {
- open_block(buffer, MR_thread_engine_base->MR_eng_id);
- }
-
- put_event_header(buffer, MR_TS_EVENT_GC_END,
- get_current_time_nanosecs());
-
- context = MR_thread_engine_base->MR_eng_this_context;
- if (!buffer->MR_tsbuffer_ctxt_is_stopped && context) {
- MR_threadscope_post_run_context_locked(buffer, context);
- }
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
- }
-}
-
-static void
-pause_thread_gc_callback(void)
-{
- struct MR_threadscope_event_buffer *buffer;
- MR_Context *context;
-
- MR_DO_THREADSCOPE_DEBUG(
- fprintf(stderr, "In gc pause thread callback thread: 0x%lx\n",
- pthread_self())
- );
- if (MR_thread_engine_base == NULL) return;
- buffer = MR_thread_engine_base->MR_eng_ts_buffer;
- if (buffer == NULL) {
- /* GC might be running before we're done setting up */
- return;
- }
-
- if (MR_US_TRY_LOCK(&(buffer->MR_tsbuffer_lock))) {
- context = MR_thread_engine_base->MR_eng_this_context;
- if (!buffer->MR_tsbuffer_ctxt_is_stopped && context) {
- MR_threadscope_post_stop_context_locked(buffer, context,
- MR_TS_STOP_REASON_YIELDING);
- }
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
- }
-}
-
-static void
-resume_thread_gc_callback(void)
-{
- struct MR_threadscope_event_buffer *buffer;
- MR_Context *context;
-
- MR_DO_THREADSCOPE_DEBUG(
- fprintf(stderr, "In gc resume thread callback thread: 0x%lx\n",
- pthread_self());
- );
- if (MR_thread_engine_base == NULL) return;
- buffer = MR_thread_engine_base->MR_eng_ts_buffer;
- if (buffer == NULL) {
- /* GC might be running before we're done setting up */
- return;
- }
-
- if (MR_US_TRY_LOCK(&(buffer->MR_tsbuffer_lock))) {
- context = MR_thread_engine_base->MR_eng_this_context;
- if (!buffer->MR_tsbuffer_ctxt_is_stopped && context) {
- MR_threadscope_post_run_context_locked(buffer, context);
- }
- MR_US_UNLOCK(&(buffer->MR_tsbuffer_lock));
- }
-}
-
-/***************************************************************************/
-
-static Time
-get_current_time_nanosecs(void)
-{
- if (MR_threadscope_use_tsc) {
- MR_uint_least64_t current_tsc;
- MercuryEngine *eng = MR_thread_engine_base;
-
- current_tsc = MR_read_cpu_tsc();
-
- /* The large constant (10^9) here converts seconds into nanoseconds. */
- return (current_tsc + eng->MR_eng_cpu_clock_ticks_offset) /
- (MR_cpu_cycles_per_sec / 1000000000);
- } else {
- return gettimeofday_nsecs() + MR_gettimeofday_offset;
- }
-}
-
-static Time
-gettimeofday_nsecs(void)
-{
- struct timeval tv;
-
- if (0 != gettimeofday(&tv, NULL)) {
- perror("gettimeofday()");
- /*
- ** Return a stupid value generating an obviously bad logfile
- ** rather than crashing a program that may otherwise work.
- */
- return 0;
- }
- return (Time)tv.tv_sec * 1000000000 +
- (Time)tv.tv_usec * 1000;
-}
-
-/***************************************************************************/
-
-#endif /* MR_THREADSCOPE */
diff --git a/runtime/mercury_threadscope.h b/runtime/mercury_threadscope.h
deleted file mode 100644
index 21df4a6..0000000
--- a/runtime/mercury_threadscope.h
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
-** vim:ts=4 sw=4 expandtab
-*/
-/*
-** Copyright (C) 2009-2011 The University of Melbourne.
-**
-** This file may only be copied under the terms of the GNU Library General
-** Public License - see the file COPYING.LIB in the Mercury distribution.
-*/
-
-/*
-** mercury_threadscope.h - defines Mercury threadscope profiling support.
-**
-** See "Parallel Preformance Tuning for Haskell" - Don Jones Jr, Simon Marlow
-** and Satnam Singh for information about threadscope.
-*/
-
-#ifndef MERCURY_THREADSCOPE_H
-#define MERCURY_THREADSCOPE_H
-
-#include "mercury_types.h" /* for MR_Word, MR_Code, etc */
-#include "mercury_engine.h"
-#include "mercury_context.h"
-
-#ifdef MR_THREADSCOPE
-
-/*
-** Reasons why a context has been stopped, not all of these apply to Mercury,
-** for instance contexts don't yield.
-*/
-#define MR_TS_STOP_REASON_HEAP_OVERFLOW 1
-#define MR_TS_STOP_REASON_STACK_OVERFLOW 2
-#define MR_TS_STOP_REASON_YIELDING 3
-#define MR_TS_STOP_REASON_BLOCKED 4
-#define MR_TS_STOP_REASON_FINISHED 5
-
-typedef struct MR_threadscope_event_buffer MR_threadscope_event_buffer_t;
-
-typedef MR_uint_least16_t MR_ContextStopReason;
-typedef MR_Integer MR_ContextId;
-typedef MR_uint_least32_t MR_TS_StringId;
-typedef MR_uint_least32_t MR_SparkId;
-typedef MR_uint_least32_t MR_EngSetId;
-typedef MR_uint_least16_t MR_EngSetType;
-typedef MR_uint_least32_t MR_TS_Pid;
-
-typedef struct MR_Threadscope_String {
- const char* MR_tsstring_string;
- MR_TS_StringId MR_tsstring_id;
-} MR_Threadscope_String;
-
-/*
-** Set this to true to use the CPU's time stamp counter.
-**
-** This is initially set in mercury_wrapper.c and may be reset by
-** MR_setup_threadscope if the TSC can't be used.
-*/
-extern MR_bool MR_threadscope_use_tsc;
-
-/*
-** This must be called by the primordial thread before starting any other
-** threads but after the primordial thread has been pinned.
-*/
-extern void MR_setup_threadscope(void);
-
-extern void MR_finalize_threadscope(void);
-
-extern void MR_threadscope_setup_engine(MercuryEngine *eng);
-
-extern void MR_threadscope_finalize_engine(MercuryEngine *eng);
-
-#if 0
-/*
-** It looks like we don't need TSC synchronization code on modern x86(-64) CPUs
-** including multi-socket systems (tested on goliath and taura). If we find
-** systems where this is needed we can enable it via a runtime check.
-*/
-/*
-** Synchronize a slave thread's TSC offset to the master's. The master thread
-** (with an engine) should call MR_threadscope_sync_tsc_master() for each slave
-** while each slave (with an engine) calls MR_threadscope_sync_tsc_slave().
-** All master - slave pairs must be pinned to CPUs and setup their threadscope
-** structures already (by calling MR_threadscope_setup_engine() above).
-** Multiple slaves may call the _slave at the same time, a lock is used to
-** synchronize only one at a time. Only the primordial thread may call
-** MR_threadscope_sync_tsc_master().
-*/
-extern void MR_threadscope_sync_tsc_master(void);
-extern void MR_threadscope_sync_tsc_slave(void);
-#endif
-
-/*
-** Use the following functions to post messages. All messages will read the
-** current engine's ID from the engine word, some messages will also read the
-** current context id from the context loaded into the current engine.
-*/
-
-/*
-** This context has been created, The context must be passed as a parameter so
-** that it doesn't have to be the current context.
-**
-** Using the MR_Context typedef here requires the inclusion of
-** mercury_context.h, creating a circular dependency
-*/
-extern void MR_threadscope_post_create_context(
- struct MR_Context_Struct *context);
-
-/*
-** The given context was created in order to execute a spark. This
-** event should be posted in addition to (and after) create_thread
-** above.
-*/
-extern void MR_threadscope_post_create_context_for_spark(
- struct MR_Context_Struct *ctxt);
-
-/*
-** This context is being released (back into a pool of free contexts). We may
-** see a new create_context or create_context_for_spark message with the same
-** context ID, such a message indicates that the context is being re-used.
-*/
-extern void MR_threadscope_post_release_context(
- struct MR_Context_Struct *context);
-
-/*
-** This context is being reused (after being released). This event is an
-** alternative to create_context above.
-*/
-extern void MR_threadscope_post_reuse_context(
- struct MR_Context_Struct *context, MR_Unsigned old_id);
-
-/*
-** This message says the context is now ready to run. Such as it's being
-** placed on the run queue after being blocked
-*/
-extern void MR_threadscope_post_context_runnable(
- struct MR_Context_Struct *context);
-
-/*
-** This message says we're now running the current context
-*/
-extern void MR_threadscope_post_run_context(void);
-
-/*
-** This message says we've stopped executing the current context,
-** a reason why should be provided.
-*/
-extern void MR_threadscope_post_stop_context(MR_ContextStopReason reason);
-
-/*
-** This message says we're about to execute a spark from our local stack.
-*/
-extern void MR_threadscope_post_run_spark(MR_SparkId spark_id);
-
-/*
-** This message says that we're about to execute a spark that was stolen from
-** another's stack.
-*/
-extern void MR_threadscope_post_steal_spark(MR_SparkId spark_id);
-
-/*
-** This message says that a spark is being created for the given computation.
-** The spark's ID is given as an argument.
-*/
-extern void MR_threadscope_post_sparking(MR_Word* dynamic_conj_id,
- MR_SparkId spark_id);
-
-/*
-** Post this message just before invoking the main/2 predicate.
-*/
-extern void MR_threadscope_post_calling_main(void);
-
-/*
-** Post this message when an engine begins looking for a context to run.
-*/
-extern void MR_threadscope_post_looking_for_global_context(void);
-
-/*
-** Post this message when an engine begins trying to run a spark from it's
-** local stack.
-*/
-extern void MR_threadscope_post_looking_for_local_spark(void);
-
-/*
-** Post this message when a thread is about to attempt work stealing.
-*/
-extern void MR_threadscope_post_work_stealing(void);
-
-/*
-** Post this message before a parallel conjunction starts.
-*/
-extern void MR_threadscope_post_start_par_conj(MR_Word* dynamic_id,
- MR_TS_StringId static_id);
-
-/*
-** Post this message after a parallel conjunction stops.
-*/
-extern void MR_threadscope_post_end_par_conj(MR_Word* dynamic_id);
-
-/*
-** Post this message when a parallel conjunct calls the bariier code.
-*/
-extern void MR_threadscope_post_end_par_conjunct(MR_Word* dynamic_id);
-
-/*
-** Post this message when a future is created, this establishes the conjuction
-** id to future id mapping. The conjunction id is inferred by context.
-** The name of the future within the conjunction is given by 'name'.
-*/
-extern void MR_threadscope_post_new_future(MR_Future* future_id, MR_TS_StringId name);
-
-/*
-** Post either of these messages when waiting on a future. THe first if the
-** context had to be suspended because the future was not available, and the
-** second when the context did not need to be suspended.
-*/
-extern void MR_threadscope_post_wait_future_nosuspend(MR_Future* future_id);
-extern void MR_threadscope_post_wait_future_suspended(MR_Future* future_id);
-
-/*
-** Post this event when signaling the production of a future.
-*/
-extern void MR_threadscope_post_signal_future(MR_Future* future_id);
-
-/*
-** Post this event when the engine is going to sleep.
-*/
-extern void MR_threadscope_post_engine_sleeping(void);
-
-/*
-** Register all the strings in an array and save their IDs in the array.
-*/
-extern void MR_threadscope_register_strings_array(MR_Threadscope_String *array,
- unsigned size);
-
-/*
-** Post a user-defined log message.
-*/
-extern void MR_threadscope_post_log_msg(const char *message);
-
-#endif /* MR_THREADSCOPE */
-
-#endif /* not MERCURY_THREADSCOPE_H */
diff --git a/runtime/mercury_wrapper.c b/runtime/mercury_wrapper.c
index 23856bb..edb72a9 100644
--- a/runtime/mercury_wrapper.c
+++ b/runtime/mercury_wrapper.c
@@ -65,7 +65,7 @@ ENDINIT
#include "mercury_memory.h" /* for MR_copy_string() */
#include "mercury_memory_handlers.h" /* for MR_default_handler */
#include "mercury_thread.h" /* for MR_debug_threads */
-#include "mercury_threadscope.h"
+#include "mercury_par_builtin.h"
#if defined(MR_HAVE__SNPRINTF) && ! defined(MR_HAVE_SNPRINTF)
#define snprintf _snprintf
@@ -713,7 +713,7 @@ mercury_runtime_init(int argc, char **argv)
#ifdef MR_THREADSCOPE
/*
** TSC Synchronization is not used, support is commented out.
- ** See runtime/mercury_threadscope.h for an explanation.
+ ** See runtime/mercury_par_profile.h for an explanation.
**
for (i = 1; i < MR_num_threads; i++) {
MR_threadscope_sync_tsc_master();
--
2.0.0.rc0
More information about the reviews
mailing list