diff --git a/mps/code/arena.c b/mps/code/arena.c index 750a27e37fa..07a753ab35f 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -289,6 +289,7 @@ static Res ArenaAbsInit(Arena arena, Size grainSize, ArgList args) if (res != ResOK) goto failMFSInit; + EventLabelPointer(ArenaCBSBlockPool(arena), EventInternString("CBSBlock")); return ResOK; failMFSInit: @@ -497,6 +498,7 @@ Res ControlInit(Arena arena) if (res != ResOK) return res; arena->poolReady = TRUE; /* */ + EventLabelPointer(&arena->controlPoolStruct, EventInternString("Control")); return ResOK; } diff --git a/mps/code/arenacl.c b/mps/code/arenacl.c index 757b4c7326c..553f82d583e 100644 --- a/mps/code/arenacl.c +++ b/mps/code/arenacl.c @@ -305,7 +305,8 @@ static Res ClientArenaCreate(Arena *arenaReturn, ArgList args) arena->zoneShift = SizeFloorLog2(size >> MPS_WORD_SHIFT); AVER(ArenaGrainSize(arena) == ChunkPageSize(arena->primary)); - EVENT3(ArenaCreateCL, arena, size, base); + EVENT6(ArenaCreateCL, arena, size, base, grainSize, + ClassOfPoly(Arena, arena), arena->serial); AVERT(ClientArena, clientArena); *arenaReturn = arena; return ResOK; diff --git a/mps/code/arenavm.c b/mps/code/arenavm.c index 78e2a67cbf4..1c2a30907a1 100644 --- a/mps/code/arenavm.c +++ b/mps/code/arenavm.c @@ -659,7 +659,8 @@ static Res VMArenaCreate(Arena *arenaReturn, ArgList args) AVER(ChunkPageSize(chunk) == ArenaGrainSize(arena)); AVERT(VMArena, vmArena); - EVENT3(ArenaCreateVM, arena, size, chunkSize); + EVENT6(ArenaCreateVM, arena, size, chunkSize, grainSize, + ClassOfPoly(Arena, arena), arena->serial); vmArena->extended(arena, chunk->base, chunkSize); diff --git a/mps/code/event.c b/mps/code/event.c index ec032640a46..5b4a11eac93 100644 --- a/mps/code/event.c +++ b/mps/code/event.c @@ -297,6 +297,16 @@ void EventLabelAddr(Addr addr, EventStringId id) } +/* EventLabelPointer -- emit event to label pointer with the given id */ + +void EventLabelPointer(Pointer pointer, EventStringId id) +{ + AVER((Serial)id < EventInternSerial); + + EVENT2(LabelPointer, pointer, id); +} + + /* Convert event parameter sort to WriteF arguments */ #define EVENT_WRITE_PARAM_MOST(name, index, sort, ident) \ @@ -492,6 +502,15 @@ void EventLabelAddr(Addr addr, Word id) } +void EventLabelPointer(Pointer pointer, Word id) +{ + UNUSED(pointer); + UNUSED(id); + /* EventLabelPointer is reached in varieties without events, but + doesn't have to do anything. */ +} + + Res EventDescribe(Event event, mps_lib_FILE *stream, Count depth) { UNUSED(event); diff --git a/mps/code/event.h b/mps/code/event.h index b4ef07ee5ca..987068feb13 100644 --- a/mps/code/event.h +++ b/mps/code/event.h @@ -32,6 +32,7 @@ extern EventControlSet EventControl(EventControlSet resetMask, extern EventStringId EventInternString(const char *label); extern EventStringId EventInternGenString(size_t, const char *label); extern void EventLabelAddr(Addr addr, Word id); +extern void EventLabelPointer(Pointer pointer, Word id); extern void EventFlush(EventKind kind); extern Res EventDescribe(Event event, mps_lib_FILE *stream, Count depth); extern Res EventWrite(Event event, mps_lib_FILE *stream); diff --git a/mps/code/eventdef.h b/mps/code/eventdef.h index 5ba56dee134..3718d04206f 100644 --- a/mps/code/eventdef.h +++ b/mps/code/eventdef.h @@ -67,7 +67,7 @@ */ #define EventNameMAX ((size_t)19) -#define EventCodeMAX ((EventCode)0x0088) +#define EventCodeMAX ((EventCode)0x0089) #define EVENT_LIST(EVENT, X) \ /* 0123456789012345678 <- don't exceed without changing EventNameMAX */ \ @@ -194,7 +194,8 @@ EVENT(X, ArenaUseFreeZone , 0x0085, TRUE, Arena) \ /* EVENT(X, ArenaBlacklistZone , 0x0086, TRUE, Arena) */ \ EVENT(X, PauseTimeSet , 0x0087, TRUE, Arena) \ - EVENT(X, TraceEndGen , 0x0088, TRUE, Trace) + EVENT(X, TraceEndGen , 0x0088, TRUE, Trace) \ + EVENT(X, LabelPointer , 0x0089, TRUE, User) /* Remember to update EventNameMAX and EventCodeMAX above! @@ -233,7 +234,10 @@ #define EVENT_ArenaCreateVM_PARAMS(PARAM, X) \ PARAM(X, 0, P, arena) \ PARAM(X, 1, W, userSize) \ - PARAM(X, 2, W, chunkSize) + PARAM(X, 2, W, chunkSize) \ + PARAM(X, 3, W, grainSize) \ + PARAM(X, 4, P, arenaClass) \ + PARAM(X, 5, U, serial) #define EVENT_ArenaCreateVMNZ_PARAMS(PARAM, X) \ PARAM(X, 0, P, arena) \ @@ -279,7 +283,10 @@ #define EVENT_ArenaCreateCL_PARAMS(PARAM, X) \ PARAM(X, 0, P, arena) \ PARAM(X, 1, W, size) \ - PARAM(X, 2, A, base) + PARAM(X, 2, A, base) \ + PARAM(X, 3, W, grainSize) \ + PARAM(X, 4, P, arenaClass) \ + PARAM(X, 5, U, serial) #define EVENT_ArenaDestroy_PARAMS(PARAM, X) \ PARAM(X, 0, P, arena) @@ -298,7 +305,8 @@ #define EVENT_PoolInit_PARAMS(PARAM, X) \ PARAM(X, 0, P, pool) \ PARAM(X, 1, P, arena) \ - PARAM(X, 2, P, poolClass) + PARAM(X, 2, P, poolClass) \ + PARAM(X, 3, U, serial) #define EVENT_PoolFinish_PARAMS(PARAM, X) \ PARAM(X, 0, P, pool) @@ -712,6 +720,10 @@ PARAM(X, 4, W, preservedInPlace) /* bytes preserved in generation */ \ PARAM(X, 5, D, mortality) /* updated mortality */ +#define EVENT_LabelPointer_PARAMS(PARAM, X) \ + PARAM(X, 0, P, pointer) \ + PARAM(X, 1, W, stringId) + #endif /* eventdef_h */ diff --git a/mps/code/eventtxt.c b/mps/code/eventtxt.c index f739c55923d..d0a6e60ca96 100644 --- a/mps/code/eventtxt.c +++ b/mps/code/eventtxt.c @@ -233,14 +233,16 @@ static char *parseString(char **pInOut) } /* Event logs have interned strings (i.e. they construct a partial - * function from non-negatie integer IDs to strings), and can label - * addresses with intern string IDs (i.e. they construct a partial - * function from address to string ID). We need two tables to keep - * track of these. */ + * function from non-negative integer IDs to strings), and can label + * addresses and pointers with intern string IDs (i.e. they construct + * a partial function from address or pointer to string ID). We need + * three tables to keep track of these. */ static Table internTable; /* dictionary of intern ids to strings */ -static Table labelTable; /* dictionary of addrs to intern ids */ +static Table labelAddrTable; /* dictionary of addrs to intern ids */ + +static Table labelPointerTable; /* dictionary of pointers to intern ids */ static void createTables(mps_pool_t pool) { @@ -255,11 +257,18 @@ static void createTables(mps_pool_t pool) everror("Couldn't make intern table."); /* We assume that 0 and 1 are invalid as Addrs. */ - res = TableCreate(&labelTable, (size_t)1<<7, + res = TableCreate(&labelAddrTable, (size_t)1<<7, tableAlloc, tableFree, pool, 0, 1); if (res != ResOK) - everror("Couldn't make label table."); + everror("Couldn't make address label table."); + + /* We assume that 0 and 1 are invalid as Pointers. */ + res = TableCreate(&labelPointerTable, (size_t)1<<7, + tableAlloc, tableFree, pool, + 0, 1); + if (res != ResOK) + everror("Couldn't make pointer label table."); } /* recordIntern -- record an interned string in the table. a copy of @@ -326,8 +335,9 @@ static size_t labelFind(LabelList list, EventClock clock) } /* recordLabel records a label: an association (made at the time given - * by 'clock') between an address and a string ID. These are encoded - * as two hexadecimal numbers in the string pointed to by 'p'. + * by 'clock') between a client address or an internal pointer and a + * string ID. These are encoded as two hexadecimal numbers in the + * string pointed to by 'p'. * * Note that the event log may have been generated on a platform with * addresses larger than Word on the current platform. If that happens @@ -344,7 +354,7 @@ static size_t labelFind(LabelList list, EventClock clock) * probably a bad idea and maybe doomed to failure. */ -static void recordLabel(mps_pool_t pool, EventClock clock, char *p) +static void recordLabel(mps_pool_t pool, Table table, EventClock clock, char *p) { ulongest_t address; LabelList list; @@ -359,7 +369,7 @@ static void recordLabel(mps_pool_t pool, EventClock clock, char *p) return; } - if (TableLookup(&tmp, labelTable, (TableKey)address)) { + if (TableLookup(&tmp, table, (TableKey)address)) { list = tmp; } else { /* First label for this address */ @@ -368,7 +378,7 @@ static void recordLabel(mps_pool_t pool, EventClock clock, char *p) everror("Can't allocate space for a label list"); list = tmp; list->n = 0; - res = TableDefine(labelTable, (TableKey)address, list); + res = TableDefine(table, (TableKey)address, list); if (res != ResOK) everror("Couldn't create a label mapping."); } @@ -400,15 +410,16 @@ static void recordLabel(mps_pool_t pool, EventClock clock, char *p) static int hexWordWidth = (MPS_WORD_WIDTH+3)/4; -/* printAddr -- output a ulongest_t in hex, with the interned string - * if the value is in the label table */ +/* printLabelled -- output a ulongest_t in hex, with the interned + * string if the value is in the table */ -static void printAddr(EventClock clock, ulongest_t addr, const char *ident) +static void printLabelled(EventClock clock, ulongest_t value, + const char *ident, Table table) { void *tmp; - - printf("%s:%0*" PRIXLONGEST, ident, hexWordWidth, addr); - if (TableLookup(&tmp, labelTable, (TableKey)addr)) { + + printf("%s:%0*" PRIXLONGEST, ident, hexWordWidth, value); + if (table != NULL && TableLookup(&tmp, table, (TableKey)value)) { LabelList list = tmp; size_t pos = labelFind(list, clock); if (pos > 0) { @@ -430,10 +441,15 @@ static void printAddr(EventClock clock, ulongest_t addr, const char *ident) #define processParamA(ident) \ val_hex = parseHex(&p); \ - printAddr(clock, val_hex, #ident); + printLabelled(clock, val_hex, #ident, labelAddrTable); -#define processParamP processParamA -#define processParamW processParamA +#define processParamP(ident) \ + val_hex = parseHex(&p); \ + printLabelled(clock, val_hex, #ident, labelPointerTable); + +#define processParamW(ident) \ + val_hex = parseHex(&p); \ + printLabelled(clock, val_hex, #ident, NULL); #define processParamU(ident) \ val_hex = parseHex(&p); \ @@ -515,7 +531,9 @@ static void readLog(mps_pool_t pool, FILE *input) if (code == EventInternCode) { recordIntern(pool, q); } else if (code == EventLabelCode) { - recordLabel(pool, clock, q); + recordLabel(pool, labelAddrTable, clock, q); + } else if (code == EventLabelPointerCode) { + recordLabel(pool, labelPointerTable, clock, q); } else if (code == EventEventInitCode) { ulongest_t major, median, minor, maxCode, maxNameLen, wordWidth, clocksPerSec; major = parseHex(&q); /* EVENT_VERSION_MAJOR */ diff --git a/mps/code/meter.c b/mps/code/meter.c index c2c8bccbe87..e3659d8a7b4 100644 --- a/mps/code/meter.c +++ b/mps/code/meter.c @@ -2,12 +2,6 @@ * * $Id$ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. - * - * TRANSGRESSIONS - * - * .trans.label: We label meters with EventLabelAddr, but of course that's - * meant for labelling Addr's. We get away with it as long as the type - * Meter is compatible with Addr. */ #include "meter.h" @@ -30,7 +24,7 @@ void MeterInit(Meter meter, const char *name, void *owner) meter->min = (Size)-1; sym = EventInternString(name); - EventLabelAddr((Addr)meter, sym); /* see .trans.label */ + EventLabelPointer(meter, sym); EVENT2(MeterInit, meter, owner); } diff --git a/mps/code/pool.c b/mps/code/pool.c index 81c155c2708..74ad31ec1c4 100644 --- a/mps/code/pool.c +++ b/mps/code/pool.c @@ -147,7 +147,8 @@ Res PoolInit(Pool pool, Arena arena, PoolClass klass, ArgList args) if (res != ResOK) return res; - EVENT3(PoolInit, pool, PoolArena(pool), ClassOfPoly(Pool, pool)); + EVENT4(PoolInit, pool, PoolArena(pool), ClassOfPoly(Pool, pool), + pool->serial); return ResOK; } diff --git a/mps/code/protocol.c b/mps/code/protocol.c index 1f3a648975c..0bc54ea4b4e 100644 --- a/mps/code/protocol.c +++ b/mps/code/protocol.c @@ -138,8 +138,7 @@ void ClassRegister(InstClass klass) /* label the pool class with its name */ EventInit(); classId = EventInternString(ClassName(klass)); - /* NOTE: this breaks */ - EventLabelAddr((Addr)klass, classId); + EventLabelPointer(klass, classId); } diff --git a/mps/tool/monitor b/mps/tool/monitor index 09785a9bcbd..f262630b756 100755 --- a/mps/tool/monitor +++ b/mps/tool/monitor @@ -8,8 +8,8 @@ import argparse -from collections import defaultdict, namedtuple -from functools import partial +from collections import namedtuple +import os from struct import Struct import sys @@ -83,11 +83,18 @@ def decode_events(read): class TimeSeries: "Series of data points in time order." - def __init__(self, model, name): + def __init__(self, model, owner, desc): self.t = [] self.y = [] + self.owner = owner + self.desc = desc model.add_time_series(self) + @property + def name(self): + "Name of time series." + return f"{self.owner.name}.{self.desc}" + def append(self, t, y): "Append data y at time t." assert not self.t or t >= self.t[-1] @@ -97,18 +104,20 @@ class TimeSeries: class Accumulator(TimeSeries): "Time series that is always non-negative and updates by accumulation." - def __init__(self, model, name, initial=0): - super().__init__(model, name) + def __init__(self, model, owner, desc, initial=0): + super().__init__(model, owner, desc) self.value = initial def add(self, t, delta): "Add delta to the accumulator at time t." + self.append(t, self.value) self.value += delta self.append(t, self.value) def sub(self, t, delta): "Subtract delta from the accumulator at time t." assert self.value >= delta + self.append(t, self.value) self.value -= delta self.append(t, self.value) @@ -128,8 +137,24 @@ class EventHandler: class Pool(EventHandler): "Model of an MPS pool." - def __init__(self, model): - self.alloc = Accumulator(model, "Bytes allocated from the arena.") + def __init__(self, arena, pointer): + self.arena = arena + self.model = arena.model + self.pointer = pointer + self.pool_class = None + self.serial = None + self.alloc = Accumulator(arena.model, self, "alloc") + + @property + def name(self): + label = self.model.label(self.pointer) + if label: + return label + class_name = self.model.label(self.pool_class)[:-4] or 'Pool' + if self.serial is not None: + return f"{class_name}[{self.serial}]" + else: + return f"{class_name}[{self.pointer:x}]" def ArenaAlloc(self, event): self.alloc.add(event.header.clock, event.size) @@ -137,57 +162,105 @@ class Pool(EventHandler): def ArenaFree(self, event): self.alloc.sub(event.header.clock, event.size) + def PoolInit(self, event): + self.pool_class = event.poolClass + self.serial = event.serial + class Arena(EventHandler): "Model of an MPS arena." - def __init__(self, model): - self.pool = defaultdict(partial(Pool, model)) # address -> Pool + def __init__(self, model, pointer): + self.model = model + self.pointer = pointer + self.arena_class = None + self.serial = None + self.pool = {} # pointer -> Pool + + @property + def name(self): + if len(self.model.arena) <= 1: + # No need to distinguish arenas if there's just one. + return "" + label = self.model.label(self.pointer) + if label: + return label + class_name = self.model.label(self.arena_class)[:-5] or 'Arena' + if self.serial is not None: + return f"{class_name}[{self.serial}]" + else: + return f"{class_name}[{self.pointer:x}]" def delegate_to_pool(self, event): "Handle a telemetry event by delegating to the pool model." - self.pool[event.pool].handle(event) + pointer = event.pool + try: + pool = self.pool[pointer] + except KeyError: + self.pool[pointer] = pool = Pool(self, pointer) + pool.handle(event) ArenaAlloc = ArenaFree = PoolInit = delegate_to_pool + def ArenaCreateVM(self, event): + self.arena_class = event.arenaClass + self.serial = event.serial + + ArenaCreateCL = ArenaCreateVM + class Model(EventHandler): "Model of an application using the MPS." def __init__(self): - self.intern = {} # stringId -> string - self.label = {} # address -> stringId - self.arena = defaultdict(partial(Arena, self)) # address -> Arena + self._intern = {} # stringId -> string + self._label = {} # address or pointer -> stringId + self.arena = {} # pointer -> Arena self.time_series = [] # List of TimeSeries def add_time_series(self, series): "Add a time series to the model." self.time_series.append(series) + def label(self, pointer): + "String labelling address or pointer, or None if unlabelled." + return self._intern.get(self._label.get(pointer)) + def plot(self): "Plot all the model's time series." plt = matplotlib.pyplot - figure = plt.figure() + figure, axes = plt.subplots() for series in self.time_series: - plt.plot(series.t, series.y) + axes.plot(series.t, series.y, label=series.name) + axes.legend() plt.show() def delegate_to_arena(self, event): "Handle a telemetry event by delegating to the arena model." - self.arena[event.arena].handle(event) + addr = event.arena + try: + arena = self.arena[addr] + except KeyError: + self.arena[addr] = arena = Arena(self, addr) + arena.handle(event) ArenaCreateVM = ArenaCreateCL = ArenaAlloc = ArenaFree = PoolInit = delegate_to_arena def Intern(self, event): - self.intern[event.stringId] = event.string + self._intern[event.stringId] = event.string.decode('ascii', 'replace') def Label(self, event): - self.label[event.address] = event.stringId + self._label[event.address] = event.stringId + + def LabelPointer(self, event): + self._label[event.pointer] = event.stringId def main(): parser = argparse.ArgumentParser(description="Memory Pool System Monitor.") - parser.add_argument('telemetry', metavar='FILENAME', nargs='?', - type=argparse.FileType('rb'), default=sys.stdin, - help="telemetry output from the MPS instance") + parser.add_argument( + 'telemetry', metavar='FILENAME', nargs='?', + type=argparse.FileType('rb'), + default=os.environ.get('MPS_TELEMETRY_FILENAME', 'mpsio.log'), + help="telemetry output from the MPS instance") args = parser.parse_args() model = Model() @@ -229,4 +302,4 @@ if __name__ == '__main__': # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # -# $Id: //info.ravenbrook.com/project/mps/branch/2018-06-20/monitor/tool/branch#1 $ +# $Id$