Commit 015d3d46 authored by chak@cse.unsw.edu.au.'s avatar chak@cse.unsw.edu.au.

Expose all EventLog events as DTrace probes

- Defines a DTrace provider, called 'HaskellEvent', that provides a probe
  for every event of the eventlog framework.
- In contrast to the original eventlog, the DTrace probes are available in
  all flavours of the runtime system (DTrace probes have virtually no
  overhead if not enabled); when -DTRACING is defined both the regular
  event log as well as DTrace probes can be used.
- Currently, Mac OS X only.  User-space DTrace probes are implemented
  differently on Mac OS X than in the original DTrace implementation.
  Nevertheless, it shouldn't be too hard to enable these probes on other
  platforms, too.
- Documentation is at http://hackage.haskell.org/trac/ghc/wiki/DTrace
parent dcba7784
......@@ -558,6 +558,16 @@ dnl ** check for patch
dnl if GNU patch is named gpatch, look for it first
AC_PATH_PROGS(PatchCmd,gpatch patch, patch)
dnl ** check for dtrace (currently only implemented for Mac OS X)
HaveDtrace=NO
AC_PATH_PROG(DtraceCmd,dtrace)
if test -n "$DtraceCmd"; then
if test "x$TargetOS_CPP-$TargetVendor_CPP" == "xdarwin-apple"; then
HaveDtrace=YES
fi
fi
AC_SUBST(HaveDtrace)
AC_PATH_PROG(HSCOLOUR,HsColour)
# HsColour is passed to Cabal, so we need a native path
if test "x$HostPlatform" = "xi386-unknown-mingw32" && \
......
......@@ -112,7 +112,7 @@
#define EVENT_GC_END 10 /* () */
#define EVENT_REQUEST_SEQ_GC 11 /* () */
#define EVENT_REQUEST_PAR_GC 12 /* () */
#define EVENT_CREATE_SPARK_THREAD 15 /* (thread, spark_thread) */
#define EVENT_CREATE_SPARK_THREAD 15 /* (spark_thread) */
#define EVENT_LOG_MSG 16 /* (message ...) */
#define EVENT_STARTUP 17 /* (num_capabilities) */
#define EVENT_BLOCK_MARKER 18 /* (size, end_time, capability) */
......@@ -148,6 +148,7 @@ typedef StgWord64 EventTimestamp; // in nanoseconds
typedef StgWord32 EventThreadID;
typedef StgWord16 EventCapNo;
typedef StgWord16 EventPayloadSize; // variable-size events
typedef StgWord16 EventThreadStatus; // status for EVENT_STOP_THREAD
#endif
......
......@@ -603,6 +603,9 @@ RANLIB = @RANLIB@
SED = @SedCmd@
SHELL = /bin/sh
HaveDtrace = @HaveDtrace@
DTRACE = @DtraceCmd@
LD = @LdCmd@
NM = @NmCmd@
......
......@@ -98,7 +98,7 @@ findSpark (Capability *cap)
cap->sparks_converted++;
// Post event for running a spark from capability's own pool.
traceSchedEvent(cap, EVENT_RUN_SPARK, cap->r.rCurrentTSO, 0);
traceEventRunSpark(cap, cap->r.rCurrentTSO);
return spark;
}
......@@ -132,8 +132,7 @@ findSpark (Capability *cap)
if (spark != NULL) {
cap->sparks_converted++;
traceSchedEvent(cap, EVENT_STEAL_SPARK,
cap->r.rCurrentTSO, robbed->no);
traceEventStealSpark(cap, cap->r.rCurrentTSO, robbed->no);
return spark;
}
......@@ -579,9 +578,9 @@ yieldCapability (Capability** pCap, Task *task)
Capability *cap = *pCap;
if (waiting_for_gc == PENDING_GC_PAR) {
traceSchedEvent(cap, EVENT_GC_START, 0, 0);
traceEventGcStart(cap);
gcWorkerThread(cap);
traceSchedEvent(cap, EVENT_GC_END, 0, 0);
traceEventGcEnd(cap);
return;
}
......@@ -788,7 +787,7 @@ shutdownCapability (Capability *cap, Task *task, rtsBool safe)
continue;
}
traceSchedEvent(cap, EVENT_SHUTDOWN, 0, 0);
traceEventShutdown(cap);
RELEASE_LOCK(&cap->lock);
break;
}
......
......@@ -1903,8 +1903,23 @@ stg_traceEventzh
{
W_ msg;
msg = R1;
#if defined(TRACING) || defined(DEBUG)
foreign "C" traceUserMsg(MyCapability() "ptr", msg "ptr") [];
#elif defined(DTRACE)
W_ enabled;
// We should go through the macro HASKELLEVENT_USER_MSG_ENABLED from
// RtsProbes.h, but that header file includes unistd.h, which doesn't
// work in Cmm
(enabled) = foreign "C" __dtrace_isenabled$HaskellEvent$user__msg$v1() [];
if (enabled != 0) {
foreign "C" dtraceUserMsgWrapper(MyCapability() "ptr", msg "ptr") [];
}
#endif
jump %ENTRY_CODE(Sp(0));
}
/* -----------------------------------------------------------------------------
*
* (c) The GHC Team, 2009
*
* User-space dtrace probes for the runtime system.
*
* ---------------------------------------------------------------------------*/
#include "HsFFI.h"
#include "rts/EventLogFormat.h"
// -----------------------------------------------------------------------------
// Payload datatypes for Haskell events
// -----------------------------------------------------------------------------
// We effectively have:
//
// typedef uint16_t EventTypeNum;
// typedef uint64_t EventTimestamp; // in nanoseconds
// typedef uint32_t EventThreadID;
// typedef uint16_t EventCapNo;
// typedef uint16_t EventPayloadSize; // variable-size events
// typedef uint16_t EventThreadStatus;
// -----------------------------------------------------------------------------
// The HaskellEvent provider captures everything from eventlog for use with
// dtrace
// -----------------------------------------------------------------------------
// These probes correspond to the events defined in EventLogFormat.h
//
provider HaskellEvent {
// scheduler events
probe create__thread (EventCapNo, EventThreadID);
probe run__thread (EventCapNo, EventThreadID);
probe stop__thread (EventCapNo, EventThreadID, EventThreadStatus);
probe thread__runnable (EventCapNo, EventThreadID);
probe migrate__thread (EventCapNo, EventThreadID, EventCapNo);
probe run__spark (EventCapNo, EventThreadID);
probe steal__spark (EventCapNo, EventThreadID, EventCapNo);
probe shutdown (EventCapNo);
probe thread_wakeup (EventCapNo, EventThreadID, EventCapNo);
probe gc__start (EventCapNo);
probe gc__end (EventCapNo);
probe request__seq__gc (EventCapNo);
probe request__par__gc (EventCapNo);
probe create__spark__thread (EventCapNo, EventThreadID);
// other events
//This one doesn't seem to be used at all at the moment:
// probe log__msg (char *);
probe startup (EventCapNo);
// we don't need EVENT_BLOCK_MARKER with dtrace
probe user__msg (EventCapNo, char *);
probe gc__idle (EventCapNo);
probe gc__work (EventCapNo);
probe gc__done (EventCapNo);
};
......@@ -149,6 +149,9 @@ hs_init(int *argc, char **argv[])
#ifdef TRACING
initTracing();
#endif
/* Dtrace events are always enabled
*/
dtraceEventStartup();
/* initialise scheduler data structures (needs to be done before
* initStorage()).
......
......@@ -468,7 +468,7 @@ run_thread:
}
#endif
traceSchedEvent(cap, EVENT_RUN_THREAD, t, 0);
traceEventRunThread(cap, t);
switch (prev_what_next) {
......@@ -518,7 +518,7 @@ run_thread:
t->saved_winerror = GetLastError();
#endif
traceSchedEvent (cap, EVENT_STOP_THREAD, t, ret);
traceEventStopThread(cap, t, ret);
#if defined(THREADED_RTS)
// If ret is ThreadBlocked, and this Task is bound to the TSO that
......@@ -778,7 +778,7 @@ schedulePushWork(Capability *cap USED_IF_THREADS,
debugTrace(DEBUG_sched, "pushing thread %lu to capability %d", (unsigned long)t->id, free_caps[i]->no);
appendToRunQueue(free_caps[i],t);
traceSchedEvent (cap, EVENT_MIGRATE_THREAD, t, free_caps[i]->no);
traceEventMigrateThread (cap, t, free_caps[i]->no);
if (t->bound) { t->bound->cap = free_caps[i]; }
t->cap = free_caps[i];
......@@ -802,7 +802,7 @@ schedulePushWork(Capability *cap USED_IF_THREADS,
if (spark != NULL) {
debugTrace(DEBUG_sched, "pushing spark %p to capability %d", spark, free_caps[i]->no);
traceSchedEvent(free_caps[i], EVENT_STEAL_SPARK, t, cap->no);
traceEventStealSpark(free_caps[i], t, cap->no);
newSpark(&(free_caps[i]->r), spark);
}
......@@ -1418,11 +1418,11 @@ scheduleDoGC (Capability *cap, Task *task USED_IF_THREADS, rtsBool force_major)
if (gc_type == PENDING_GC_SEQ)
{
traceSchedEvent(cap, EVENT_REQUEST_SEQ_GC, 0, 0);
traceEventRequestSeqGc(cap);
}
else
{
traceSchedEvent(cap, EVENT_REQUEST_PAR_GC, 0, 0);
traceEventRequestParGc(cap);
debugTrace(DEBUG_sched, "ready_to_gc, grabbing GC threads");
}
......@@ -1478,8 +1478,8 @@ delete_threads_and_gc:
heap_census = scheduleNeedHeapProfile(rtsTrue);
traceEventGcStart(cap);
#if defined(THREADED_RTS)
traceSchedEvent(cap, EVENT_GC_START, 0, 0);
// reset waiting_for_gc *before* GC, so that when the GC threads
// emerge they don't immediately re-enter the GC.
waiting_for_gc = 0;
......@@ -1487,7 +1487,7 @@ delete_threads_and_gc:
#else
GarbageCollect(force_major || heap_census, 0, cap);
#endif
traceSchedEvent(cap, EVENT_GC_END, 0, 0);
traceEventGcEnd(cap);
if (recent_activity == ACTIVITY_INACTIVE && force_major)
{
......@@ -1806,7 +1806,7 @@ suspendThread (StgRegTable *reg)
task = cap->running_task;
tso = cap->r.rCurrentTSO;
traceSchedEvent(cap, EVENT_STOP_THREAD, tso, THREAD_SUSPENDED_FOREIGN_CALL);
traceEventStopThread(cap, tso, THREAD_SUSPENDED_FOREIGN_CALL);
// XXX this might not be necessary --SDM
tso->what_next = ThreadRunGHC;
......@@ -1869,7 +1869,7 @@ resumeThread (void *task_)
task->suspended_tso = NULL;
tso->_link = END_TSO_QUEUE; // no write barrier reqd
traceSchedEvent(cap, EVENT_RUN_THREAD, tso, tso->what_next);
traceEventRunThread(cap, tso);
if (tso->why_blocked == BlockedOnCCall) {
// avoid locking the TSO if we don't have to
......@@ -1925,7 +1925,7 @@ scheduleThreadOn(Capability *cap, StgWord cpu USED_IF_THREADS, StgTSO *tso)
if (cpu == cap->no) {
appendToRunQueue(cap,tso);
} else {
traceSchedEvent (cap, EVENT_MIGRATE_THREAD, tso, capabilities[cpu].no);
traceEventMigrateThread (cap, tso, capabilities[cpu].no);
wakeupThreadOnCapability(cap, &capabilities[cpu], tso);
}
#else
......
......@@ -139,7 +139,7 @@ appendToRunQueue (Capability *cap, StgTSO *tso)
setTSOLink(cap, cap->run_queue_tl, tso);
}
cap->run_queue_tl = tso;
traceSchedEvent (cap, EVENT_THREAD_RUNNABLE, tso, 0);
traceEventThreadRunnable (cap, tso);
}
/* Push a thread on the beginning of the run queue.
......
......@@ -47,7 +47,7 @@ createSparkThread (Capability *cap)
tso = createIOThread (cap, RtsFlags.GcFlags.initialStkSize,
&base_GHCziConc_runSparks_closure);
traceSchedEvent(cap, EVENT_CREATE_SPARK_THREAD, 0, tso->id);
traceEventCreateSparkThread(cap, tso->id);
appendToRunQueue(cap,tso);
}
......
......@@ -107,7 +107,7 @@ createThread(Capability *cap, nat size)
RELEASE_LOCK(&sched_mutex);
// ToDo: report the stack size in the event?
traceSchedEvent (cap, EVENT_CREATE_THREAD, tso, tso->stack_size);
traceEventCreateThread(cap, tso);
return tso;
}
......@@ -254,7 +254,7 @@ unblockOne_ (Capability *cap, StgTSO *tso,
cap->context_switch = 1;
#endif
traceSchedEvent (cap, EVENT_THREAD_WAKEUP, tso, tso->cap->no);
traceEventThreadWakeup (cap, tso, tso->cap->no);
return next;
}
......
......@@ -9,10 +9,11 @@
// external headers
#include "Rts.h"
#ifdef TRACING
// internal headers
#include "Trace.h"
#ifdef TRACING
#include "GetTime.h"
#include "Stats.h"
#include "eventlog/EventLog.h"
......@@ -310,6 +311,7 @@ void traceUserMsg(Capability *cap, char *msg)
postUserMsg(cap, msg);
}
}
dtraceUserMsg(cap->no, msg);
}
void traceThreadStatus_ (StgTSO *tso USED_IF_DEBUG)
......@@ -345,3 +347,15 @@ void traceEnd (void)
#endif /* DEBUG */
#endif /* TRACING */
// If DTRACE is enabled, but neither DEBUG nor TRACING, we need a C land
// wrapper for the user-msg probe (as we can't expand that in PrimOps.cmm)
//
#if !defined(DEBUG) && !defined(TRACING) && defined(DTRACE)
void dtraceUserMsgWrapper(Capability *cap, char *msg)
{
dtraceUserMsg(cap->no, msg);
}
#endif /* !defined(DEBUG) && !defined(TRACING) && defined(DTRACE) */
This diff is collapsed.
......@@ -13,7 +13,6 @@
#include "Trace.h"
#include "Capability.h"
#include "Trace.h"
#include "RtsUtils.h"
#include "Stats.h"
#include "EventLog.h"
......
......@@ -70,6 +70,11 @@ rts/dist/build/sm/Scav_thr.c : rts/sm/Scav.c | $$(dir $$@)/.
rts_H_FILES = $(wildcard includes/*.h) $(wildcard rts/*.h)
ifeq "$(HaveDtrace)" "YES"
DTRACEPROBES_H = rts/dist/build/RtsProbes.h
rts_H_FILES += $(DTRACEPROBES_H)
endif
# collect the -l flags that we need to link the rts dyn lib.
rts/libs.depend : $(GHC_PKG_INPLACE)
"$(GHC_PKG_INPLACE)" field rts extra-libraries \
......@@ -362,9 +367,15 @@ rts_dist_C_SRCS = $(rts_C_SRCS) $(rts_thr_EXTRA_C_SRCS)
rts_dist_S_SRCS = $(rts_S_SRCS)
rts_dist_C_FILES = $(rts_C_SRCS) $(rts_thr_EXTRA_C_SRCS) $(rts_S_SRCS)
ifeq "$(HaveDtrace)" "YES"
rts_dist_MKDEPENDC_OPTS += -Irts/dist/build
endif
$(eval $(call build-dependencies,rts,dist))
$(rts_dist_depfile_c_asm) : libffi/dist-install/build/ffi.h
$(rts_dist_depfile_c_asm) : libffi/dist-install/build/ffi.h $(DTRACEPROBES_H)
#-----------------------------------------------------------------------------
# libffi stuff
......@@ -382,6 +393,20 @@ DYNWRAPPER_PROG = rts/dyn-wrapper$(exeext)
$(DYNWRAPPER_PROG): $(DYNWRAPPER_SRC)
"$(HC)" -cpp -optc-include -optcdyn-wrapper-patchable-behaviour.h $(INPLACE_EXTRA_FLAGS) $< -o $@
# -----------------------------------------------------------------------------
# compile dtrace probes if dtrace is supported
ifeq "$(HaveDtrace)" "YES"
rts_CC_OPTS += -DDTRACE
rts_HC_OPTS += -DDTRACE
DTRACEPROBES_SRC = rts/RtsProbes.d
$(DTRACEPROBES_H): $(DTRACEPROBES_SRC) | $(dir $@)/.
"$(DTRACE)" $(filter -I%,$(rts_CC_OPTS)) -C -h -o $@ -s $<
endif
# -----------------------------------------------------------------------------
# build the static lib containing the C main symbol
......
......@@ -1008,7 +1008,7 @@ scavenge_until_all_done (void)
loop:
traceEvent(&capabilities[gct->thread_index], EVENT_GC_WORK);
traceEventGcWork(&capabilities[gct->thread_index]);
#if defined(THREADED_RTS)
if (n_gc_threads > 1) {
......@@ -1023,7 +1023,7 @@ loop:
// scavenge_loop() only exits when there's no work to do
r = dec_running();
traceEvent(&capabilities[gct->thread_index], EVENT_GC_IDLE);
traceEventGcIdle(&capabilities[gct->thread_index]);
debugTrace(DEBUG_gc, "%d GC threads still running", r);
......@@ -1039,7 +1039,7 @@ loop:
// scavenge_loop() to perform any pending work.
}
traceEvent(&capabilities[gct->thread_index], EVENT_GC_DONE);
traceEventGcDone(&capabilities[gct->thread_index]);
}
#if defined(THREADED_RTS)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment