1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-03-26 08:41:47 -07:00

Label the time series in the monitor.

In telemetry output, distinguish between labelling of (client) addresses and (MPS-internal) pointers, to avoid transgressing the distinction.
Add arena grain size, class, and serial number to ArenaCreate* events.
Add pool class and serial number to PoolInit event.

Copied from Perforce
 Change: 194086
This commit is contained in:
Gareth Rees 2018-06-22 11:05:46 +01:00
parent 6fd9abf9bd
commit c3a7104eff
11 changed files with 182 additions and 61 deletions

View file

@ -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; /* <design/arena/#pool.ready> */
EventLabelPointer(&arena->controlPoolStruct, EventInternString("Control"));
return ResOK;
}

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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 */

View file

@ -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 */

View file

@ -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);
}

View file

@ -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;
}

View file

@ -138,8 +138,7 @@ void ClassRegister(InstClass klass)
/* label the pool class with its name */
EventInit();
classId = EventInternString(ClassName(klass));
/* NOTE: this breaks <design/type/#addr.use> */
EventLabelAddr((Addr)klass, classId);
EventLabelPointer(klass, classId);
}

View file

@ -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$