mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-18 03:40:47 -08:00
557 lines
16 KiB
C
557 lines
16 KiB
C
/* event.c: EVENT LOGGING
|
|
*
|
|
* $Id$
|
|
* Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
|
|
*
|
|
* .sources: mps.design.event
|
|
*
|
|
* TRANSGRESSIONS (rule.impl.trans)
|
|
*
|
|
* .trans.ref: The reference counting used to destroy the mps_io object
|
|
* isn't right.
|
|
*
|
|
* .trans.log: The log file will be re-created if the lifetimes of
|
|
* arenas don't overlap, but shared if they do. mps_io_create cannot
|
|
* be called twice, but EventInit avoids this anyway.
|
|
*
|
|
* .trans.ifdef: This file should logically be split into two, event.c
|
|
* (which contains NOOP definitions, for general use) and eventdl.c, which
|
|
* is specific to the logging variety and actually does logging (maybe).
|
|
* Unfortunately, the build system doesn't really cope, and so this file
|
|
* consists of two versions which are conditional on the EVENT symbol.
|
|
*/
|
|
|
|
#include "mpm.h"
|
|
#include "event.h"
|
|
#include "mpsio.h"
|
|
|
|
SRCID(event, "$Id$");
|
|
|
|
|
|
#ifdef EVENT /* .trans.ifdef */
|
|
|
|
|
|
static Bool eventInited = FALSE;
|
|
static Bool eventIOInited = FALSE;
|
|
static mps_io_t eventIO;
|
|
static Count eventUserCount;
|
|
static Serial EventInternSerial;
|
|
|
|
/* Buffers in which events are recorded, from the top down. */
|
|
char EventBuffer[EventKindLIMIT][EventBufferSIZE];
|
|
|
|
/* Pointers to last event logged into each buffer. */
|
|
char *EventLast[EventKindLIMIT];
|
|
|
|
/* Pointers to the last even written out of each buffer. */
|
|
char *EventWritten[EventKindLIMIT];
|
|
|
|
EventControlSet EventKindControl; /* Bit set used to control output. */
|
|
|
|
|
|
/* A single event structure output once per buffer flush. */
|
|
EventEventClockSyncStruct eventClockSyncStruct;
|
|
|
|
|
|
/* eventClockSync -- Populate and write the clock sync event. */
|
|
|
|
static Res eventClockSync(void)
|
|
{
|
|
Res res;
|
|
size_t size;
|
|
|
|
size= size_tAlignUp(sizeof(eventClockSyncStruct), MPS_PF_ALIGN);
|
|
eventClockSyncStruct.code = EventEventClockSyncCode;
|
|
eventClockSyncStruct.size = (EventSize)size;
|
|
EVENT_CLOCK(eventClockSyncStruct.clock);
|
|
eventClockSyncStruct.f0 = (Word)mps_clock();
|
|
res = (Res)mps_io_write(eventIO, (void *)&eventClockSyncStruct, size);
|
|
if (res != ResOK)
|
|
goto failWrite;
|
|
|
|
res = ResOK;
|
|
failWrite:
|
|
return res;
|
|
}
|
|
|
|
|
|
/* EventFlush -- flush event buffer (perhaps to the event stream) */
|
|
|
|
void EventFlush(EventKind kind)
|
|
{
|
|
AVER(eventInited);
|
|
AVER(NONNEGATIVE(kind));
|
|
AVER(kind < EventKindLIMIT);
|
|
|
|
AVER(EventBuffer[kind] <= EventLast[kind]);
|
|
AVER(EventLast[kind] <= EventWritten[kind]);
|
|
AVER(EventWritten[kind] <= EventBuffer[kind] + EventBufferSIZE);
|
|
|
|
/* Send all pending events to the event stream. */
|
|
EventSync();
|
|
|
|
/* Flush the in-memory buffer whether or not we send this buffer, so
|
|
that we can continue to record recent events. */
|
|
EventLast[kind] = EventWritten[kind] = EventBuffer[kind] + EventBufferSIZE;
|
|
}
|
|
|
|
|
|
/* EventSync -- synchronize the event stream with the buffers */
|
|
|
|
void EventSync(void)
|
|
{
|
|
EventKind kind;
|
|
Bool wrote = FALSE;
|
|
|
|
for (kind = 0; kind < EventKindLIMIT; ++kind) {
|
|
|
|
/* Is event logging enabled for this kind of event, or are or are we just
|
|
writing to the buffer for backtraces, cores, and other debugging? */
|
|
if (BS_IS_MEMBER(EventKindControl, kind)) {
|
|
size_t size;
|
|
Res res;
|
|
|
|
AVER(EventBuffer[kind] <= EventLast[kind]);
|
|
AVER(EventLast[kind] <= EventWritten[kind]);
|
|
AVER(EventWritten[kind] <= EventBuffer[kind] + EventBufferSIZE);
|
|
|
|
size = (size_t)(EventWritten[kind] - EventLast[kind]);
|
|
if (size > 0) {
|
|
|
|
/* Ensure the IO stream is open. We do this late so that no stream is
|
|
created if no events are enabled by telemetry control. */
|
|
if (!eventIOInited) {
|
|
res = (Res)mps_io_create(&eventIO);
|
|
if(res != ResOK) {
|
|
/* TODO: Consider taking some other action if open fails. */
|
|
return;
|
|
}
|
|
eventIOInited = TRUE;
|
|
}
|
|
|
|
/* Writing might be faster if the size is aligned to a multiple of the
|
|
C library or kernel's buffer size. We could pad out the buffer with
|
|
a marker for this purpose. */
|
|
|
|
res = (Res)mps_io_write(eventIO, (void *)EventLast[kind], size);
|
|
if (res == ResOK) {
|
|
/* TODO: Consider taking some other action if a write fails. */
|
|
EventWritten[kind] = EventLast[kind];
|
|
wrote = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If we wrote out events, send an EventClockSync event and flush
|
|
the telemetry stream. */
|
|
if (wrote) {
|
|
(void)eventClockSync();
|
|
(void)mps_io_flush(eventIO);
|
|
}
|
|
}
|
|
|
|
|
|
/* EventInit -- start using the event system, initialize if necessary */
|
|
|
|
void EventInit(void)
|
|
{
|
|
/* Make local enums for all event params in order to check that the indexes
|
|
in the parameter definition macros are in order, and that parameter
|
|
idents are unique. */
|
|
|
|
#define EVENT_CHECK_ENUM_PARAM(name, index, sort, ident) \
|
|
Event##name##Param##ident,
|
|
|
|
#define EVENT_CHECK_ENUM(X, name, code, always, kind) \
|
|
enum Event##name##ParamEnum { \
|
|
EVENT_##name##_PARAMS(EVENT_CHECK_ENUM_PARAM, name) \
|
|
Event##name##ParamLIMIT \
|
|
};
|
|
|
|
EVENT_LIST(EVENT_CHECK_ENUM, X)
|
|
|
|
/* Check consistency of the event definitions. These are all compile-time
|
|
checks and should get optimised away. */
|
|
|
|
#define EVENT_PARAM_CHECK_P(name, index, ident)
|
|
#define EVENT_PARAM_CHECK_A(name, index, ident)
|
|
#define EVENT_PARAM_CHECK_W(name, index, ident)
|
|
#define EVENT_PARAM_CHECK_U(name, index, ident)
|
|
#define EVENT_PARAM_CHECK_D(name, index, ident)
|
|
#define EVENT_PARAM_CHECK_B(name, index, ident)
|
|
#define EVENT_PARAM_CHECK_S(name, index, ident) \
|
|
AVER(index + 1 == Event##name##ParamLIMIT); /* strings must come last */
|
|
|
|
#define EVENT_PARAM_CHECK(name, index, sort, ident) \
|
|
AVER(index == Event##name##Param##ident); \
|
|
AVER(sizeof(EventF##sort) >= 0); /* check existence of type */ \
|
|
EVENT_PARAM_CHECK_##sort(name, index, ident)
|
|
|
|
#define EVENT_CHECK(X, name, code, always, kind) \
|
|
AVER(size_tAlignUp(sizeof(Event##name##Struct), MPS_PF_ALIGN) \
|
|
<= EventSizeMAX); \
|
|
AVER(Event##name##Code == code); \
|
|
AVER(0 <= code && code <= EventCodeMAX); \
|
|
AVER(sizeof(#name) - 1 <= EventNameMAX); \
|
|
AVER((Bool)Event##name##Always == always); \
|
|
AVERT(Bool, always); \
|
|
AVER(0 <= Event##name##Kind); \
|
|
AVER((EventKind)Event##name##Kind < EventKindLIMIT); \
|
|
EVENT_##name##_PARAMS(EVENT_PARAM_CHECK, name)
|
|
|
|
EVENT_LIST(EVENT_CHECK, X);
|
|
|
|
/* Ensure that no event can be larger than the maximum event size. */
|
|
AVER(EventBufferSIZE <= EventSizeMAX);
|
|
|
|
/* Only if this is the first call. */
|
|
if(!eventInited) { /* See .trans.log */
|
|
EventKind kind;
|
|
for (kind = 0; kind < EventKindLIMIT; ++kind) {
|
|
AVER(EventLast[kind] == NULL);
|
|
AVER(EventWritten[kind] == NULL);
|
|
EventLast[kind] = EventWritten[kind] = EventBuffer[kind] + EventBufferSIZE;
|
|
}
|
|
eventUserCount = (Count)1;
|
|
eventInited = TRUE;
|
|
EventKindControl = (Word)mps_lib_telemetry_control();
|
|
EventInternSerial = (Serial)1; /* 0 is reserved */
|
|
(void)EventInternString(MPSVersion()); /* emit version */
|
|
EVENT7(EventInit, EVENT_VERSION_MAJOR, EVENT_VERSION_MEDIAN,
|
|
EVENT_VERSION_MINOR, EventCodeMAX, EventNameMAX, MPS_WORD_WIDTH,
|
|
mps_clocks_per_sec());
|
|
/* flush these initial events to get the first ClockSync out. */
|
|
EventSync();
|
|
} else {
|
|
++eventUserCount;
|
|
}
|
|
}
|
|
|
|
|
|
/* EventFinish -- stop using the event system */
|
|
|
|
void EventFinish(void)
|
|
{
|
|
AVER(eventInited);
|
|
AVER(eventUserCount > 0);
|
|
|
|
EventSync();
|
|
|
|
--eventUserCount;
|
|
}
|
|
|
|
|
|
/* EventControl -- Change or read control word
|
|
*
|
|
* Resets the bits specified in resetMask, and flips those in
|
|
* flipMask. Returns old value.
|
|
*
|
|
* Operations can be implemented as follows:
|
|
* Set(M) EventControl(M,M)
|
|
* Reset(M) EventControl(M,0)
|
|
* Flip(M) EventControl(0,M)
|
|
* Read() EventControl(0,0)
|
|
*
|
|
* TODO: Candy-machine interface is a transgression.
|
|
*/
|
|
|
|
EventControlSet EventControl(EventControlSet resetMask,
|
|
EventControlSet flipMask)
|
|
{
|
|
EventControlSet oldValue = EventKindControl;
|
|
|
|
/* EventKindControl = (EventKindControl & ~resetMask) ^ flipMask */
|
|
EventKindControl =
|
|
BS_SYM_DIFF(BS_DIFF(EventKindControl, resetMask), flipMask);
|
|
|
|
return oldValue;
|
|
}
|
|
|
|
|
|
/* EventInternString -- emit an Intern event on the (null-term) string given */
|
|
|
|
EventStringId EventInternString(const char *label)
|
|
{
|
|
AVER(label != NULL);
|
|
return EventInternGenString(StringLength(label), label);
|
|
}
|
|
|
|
|
|
/* EventInternGenString -- emit an Intern event on the string given */
|
|
|
|
EventStringId EventInternGenString(size_t len, const char *label)
|
|
{
|
|
EventStringId id;
|
|
|
|
AVER(label != NULL);
|
|
|
|
id = EventInternSerial;
|
|
++EventInternSerial;
|
|
|
|
EVENT2S(Intern, id, len, label);
|
|
|
|
return id;
|
|
}
|
|
|
|
|
|
/* EventLabelAddr -- emit event to label address with the given id */
|
|
|
|
void EventLabelAddr(Addr addr, EventStringId id)
|
|
{
|
|
AVER((Serial)id < EventInternSerial);
|
|
|
|
EVENT2(Label, addr, id);
|
|
}
|
|
|
|
|
|
/* Convert event parameter sort to WriteF arguments */
|
|
|
|
#define EVENT_WRITE_PARAM_MOST(name, index, sort, ident) \
|
|
" $"#sort, (WriteF##sort)event->name.f##index,
|
|
#define EVENT_WRITE_PARAM_A EVENT_WRITE_PARAM_MOST
|
|
#define EVENT_WRITE_PARAM_P EVENT_WRITE_PARAM_MOST
|
|
#define EVENT_WRITE_PARAM_U EVENT_WRITE_PARAM_MOST
|
|
#define EVENT_WRITE_PARAM_W EVENT_WRITE_PARAM_MOST
|
|
#define EVENT_WRITE_PARAM_D EVENT_WRITE_PARAM_MOST
|
|
#define EVENT_WRITE_PARAM_S EVENT_WRITE_PARAM_MOST
|
|
#define EVENT_WRITE_PARAM_B(name, index, sort, ident) \
|
|
" $U", (WriteFU)event->name.f##index,
|
|
|
|
|
|
Res EventDescribe(Event event, mps_lib_FILE *stream)
|
|
{
|
|
Res res;
|
|
|
|
/* TODO: Some sort of EventCheck would be good */
|
|
if (event == NULL)
|
|
return ResFAIL;
|
|
if (stream == NULL)
|
|
return ResFAIL;
|
|
|
|
res = WriteF(stream,
|
|
"Event $P {\n", (WriteFP)event,
|
|
" code $U\n", (WriteFU)event->any.code,
|
|
" clock ", NULL);
|
|
if (res != ResOK) return res;
|
|
res = EVENT_CLOCK_WRITE(stream, event->any.clock);
|
|
if (res != ResOK) return res;
|
|
res = WriteF(stream, "\n size $U\n", (WriteFU)event->any.size, NULL);
|
|
if (res != ResOK) return res;
|
|
|
|
switch (event->any.code) {
|
|
|
|
#define EVENT_DESC_PARAM(name, index, sort, ident) \
|
|
"\n $S", (WriteFS)#ident, \
|
|
EVENT_WRITE_PARAM_##sort(name, index, sort, ident)
|
|
|
|
#define EVENT_DESC(X, name, _code, always, kind) \
|
|
case _code: \
|
|
res = WriteF(stream, \
|
|
" event \"$S\"", (WriteFS)#name, \
|
|
EVENT_##name##_PARAMS(EVENT_DESC_PARAM, name) \
|
|
NULL); \
|
|
if (res != ResOK) return res; \
|
|
break;
|
|
|
|
EVENT_LIST(EVENT_DESC, X)
|
|
|
|
default:
|
|
res = WriteF(stream, " event type unknown", NULL);
|
|
if (res != ResOK) return res;
|
|
/* TODO: Hexdump unknown event contents. */
|
|
break;
|
|
}
|
|
|
|
res = WriteF(stream,
|
|
"\n} Event $P\n", (WriteFP)event,
|
|
NULL);
|
|
return res;
|
|
}
|
|
|
|
|
|
Res EventWrite(Event event, mps_lib_FILE *stream)
|
|
{
|
|
Res res;
|
|
|
|
if (event == NULL) return ResFAIL;
|
|
if (stream == NULL) return ResFAIL;
|
|
|
|
res = EVENT_CLOCK_WRITE(stream, event->any.clock);
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
switch (event->any.code) {
|
|
|
|
#define EVENT_WRITE_PARAM(name, index, sort, ident) \
|
|
EVENT_WRITE_PARAM_##sort(name, index, sort, ident)
|
|
|
|
#define EVENT_WRITE(X, name, code, always, kind) \
|
|
case code: \
|
|
res = WriteF(stream, " $S", #name, \
|
|
EVENT_##name##_PARAMS(EVENT_WRITE_PARAM, name) \
|
|
NULL); \
|
|
if (res != ResOK) return res; \
|
|
break;
|
|
EVENT_LIST(EVENT_WRITE, X)
|
|
|
|
default:
|
|
res = WriteF(stream, " <unknown code $U>", event->any.code, NULL);
|
|
if (res != ResOK) return res;
|
|
/* TODO: Hexdump unknown event contents. */
|
|
break;
|
|
}
|
|
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
void EventDump(mps_lib_FILE *stream)
|
|
{
|
|
Event event;
|
|
EventKind kind;
|
|
|
|
AVER(stream != NULL);
|
|
|
|
/* This can happen if there's a backtrace very early in the life of
|
|
the MPS, and will cause an access violation if we continue. */
|
|
if (!eventInited) {
|
|
(void)WriteF(stream, "No events\n", NULL);
|
|
return;
|
|
}
|
|
|
|
for (kind = 0; kind < EventKindLIMIT; ++kind) {
|
|
for (event = (Event)EventLast[kind];
|
|
event < (Event)(EventBuffer[kind] + EventBufferSIZE);
|
|
event = (Event)((char *)event + event->any.size)) {
|
|
/* Try to keep going even if there's an error, because this is used as a
|
|
backtrace and we'll take what we can get. */
|
|
(void)EventWrite(event, stream);
|
|
(void)WriteF(stream, "\n", NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#else /* EVENT, not */
|
|
|
|
|
|
void EventSync(void)
|
|
{
|
|
NOOP;
|
|
}
|
|
|
|
|
|
void EventInit(void)
|
|
{
|
|
NOOP;
|
|
}
|
|
|
|
|
|
void EventFinish(void)
|
|
{
|
|
NOOP;
|
|
}
|
|
|
|
|
|
EventControlSet EventControl(EventControlSet resetMask,
|
|
EventControlSet flipMask)
|
|
{
|
|
UNUSED(resetMask);
|
|
UNUSED(flipMask);
|
|
return BS_EMPTY(EventControlSet);
|
|
}
|
|
|
|
|
|
EventStringId EventInternString(const char *label)
|
|
{
|
|
UNUSED(label);
|
|
/* EventInternString is reached in varieties without events, but the result
|
|
is not used for anything. */
|
|
return (EventStringId)0x4026EAC8;
|
|
}
|
|
|
|
|
|
Word EventInternGenString(size_t len, const char *label)
|
|
{
|
|
UNUSED(len); UNUSED(label);
|
|
/* EventInternGenString is reached in varieties without events, but
|
|
the result is not used for anything. */
|
|
return (EventStringId)0x4026EAC8;
|
|
}
|
|
|
|
|
|
void EventLabelAddr(Addr addr, Word id)
|
|
{
|
|
UNUSED(addr);
|
|
UNUSED(id);
|
|
/* EventLabelAddr is reached in varieties without events, but doesn't have
|
|
to do anything. */
|
|
}
|
|
|
|
|
|
Res EventDescribe(Event event, mps_lib_FILE *stream)
|
|
{
|
|
UNUSED(event);
|
|
UNUSED(stream);
|
|
return ResUNIMPL;
|
|
}
|
|
|
|
|
|
Res EventWrite(Event event, mps_lib_FILE *stream)
|
|
{
|
|
UNUSED(event);
|
|
UNUSED(stream);
|
|
return ResUNIMPL;
|
|
}
|
|
|
|
|
|
extern void EventDump(mps_lib_FILE *stream)
|
|
{
|
|
UNUSED(stream);
|
|
}
|
|
|
|
|
|
#endif /* EVENT */
|
|
|
|
|
|
/* C. COPYRIGHT AND LICENSE
|
|
*
|
|
* Copyright (C) 2001-2014 Ravenbrook Limited <http://www.ravenbrook.com/>.
|
|
* All rights reserved. This is an open source license. Contact
|
|
* Ravenbrook for commercial licensing options.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Redistributions in any form must be accompanied by information on how
|
|
* to obtain complete source code for this software and any accompanying
|
|
* software that uses this software. The source code must either be
|
|
* included in the distribution or be available for no more than the cost
|
|
* of distribution plus a nominal fee, and must be freely redistributable
|
|
* under reasonable conditions. For an executable file, complete source
|
|
* code means the source code for all modules it contains. It does not
|
|
* include source code for modules or files that typically accompany the
|
|
* major components of the operating system on which the executable file
|
|
* runs.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|