Files
mercury/runtime/mercury_threadscope.c
Mark Brown d465fa53cb Update the COPYING.LIB file and references to it.
Discussion of these changes can be found on the Mercury developers
mailing list archives from June 2018.

COPYING.LIB:
    Add a special linking exception to the LGPL.

*:
    Update references to COPYING.LIB.

    Clean up some minor errors that have accumulated in copyright
    messages.
2018-06-09 17:43:12 +10:00

2202 lines
68 KiB
C

// vim: ts=4 sw=4 expandtab ft=c
// Copyright (C) 2008-2009 The GHC Team.
// Copyright (C) 2009-2011 The University of Melbourne.
// Copyright (C) 2012, 2014-2016, 2018 The Mercury team.
// This file is distributed under the terms specified in COPYING.LIB.
// 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 separate
// 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-using 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 retroactively 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;
// Alternatively, 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 separate 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 preceded
// 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 preceded
// 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_ws_engines);
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-stealing 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) {
MR_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)) {
MR_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))
{
MR_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 are 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);
}
// Ideally 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 are 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 are 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 are 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)) {
MR_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