From e323bd2e15b1bf933e82f768aeff0dfcb0639842 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 26 Jun 2018 16:21:48 +0100 Subject: [PATCH] New events arenapollbegin and arenapollend. Measure fraction of CPU time in polling. Represent the units of each TimeSeries. Draw graphs for up to two different units using Axes.twinx. Copied from Perforce Change: 194204 --- mps/code/eventdef.h | 19 +++++----- mps/code/global.c | 4 +-- mps/tool/monitor | 84 +++++++++++++++++++++++++++++++++------------ 3 files changed, 76 insertions(+), 31 deletions(-) diff --git a/mps/code/eventdef.h b/mps/code/eventdef.h index 58bb7373a68..30098ac899e 100644 --- a/mps/code/eventdef.h +++ b/mps/code/eventdef.h @@ -67,7 +67,7 @@ */ #define EventNameMAX ((size_t)19) -#define EventCodeMAX ((EventCode)0x0089) +#define EventCodeMAX ((EventCode)0x008B) #define EVENT_LIST(EVENT, X) \ /* 0123456789012345678 <- don't exceed without changing EventNameMAX */ \ @@ -182,7 +182,7 @@ EVENT(X, EventInit , 0x0074, TRUE, Arena) \ EVENT(X, EventClockSync , 0x0075, TRUE, Arena) \ EVENT(X, ArenaAccess , 0x0076, TRUE, Arena) \ - EVENT(X, ArenaPoll , 0x0077, TRUE, Arena) \ + /* EVENT(X, ArenaPoll , 0x0077, TRUE, Arena) */ \ EVENT(X, ArenaSetEmergency , 0x0078, TRUE, Arena) \ EVENT(X, VMCompact , 0x0079, TRUE, Arena) \ EVENT(X, amcScanNailed , 0x0080, TRUE, Seg) \ @@ -195,7 +195,9 @@ /* EVENT(X, ArenaBlacklistZone , 0x0086, TRUE, Arena) */ \ EVENT(X, PauseTimeSet , 0x0087, TRUE, Arena) \ EVENT(X, TraceEndGen , 0x0088, TRUE, Trace) \ - EVENT(X, LabelPointer , 0x0089, TRUE, User) + EVENT(X, LabelPointer , 0x0089, TRUE, User) \ + EVENT(X, ArenaPollBegin , 0x008A, TRUE, Arena) \ + EVENT(X, ArenaPollEnd , 0x008B, TRUE, Arena) /* Remember to update EventNameMAX and EventCodeMAX above! @@ -645,11 +647,6 @@ PARAM(X, 2, P, addr) \ PARAM(X, 3, U, mode) -#define EVENT_ArenaPoll_PARAMS(PARAM, X) \ - PARAM(X, 0, P, arena) \ - PARAM(X, 1, W, start) \ - PARAM(X, 2, B, workWasDone) - #define EVENT_ArenaSetEmergency_PARAMS(PARAM, X) \ PARAM(X, 0, P, arena) \ PARAM(X, 1, B, emergency) @@ -720,6 +717,12 @@ PARAM(X, 0, P, pointer) /* pointer */ \ PARAM(X, 1, W, stringId) /* string identifier of its label */ +#define EVENT_ArenaPollBegin_PARAMS(PARAM, X) \ + PARAM(X, 0, P, arena) /* arena about to be polled */ + +#define EVENT_ArenaPollEnd_PARAMS(PARAM, X) \ + PARAM(X, 0, P, arena) /* arena that was polled */ \ + PARAM(X, 1, B, workWasDone) /* any collection work done in poll? */ #endif /* eventdef_h */ diff --git a/mps/code/global.c b/mps/code/global.c index f992f138480..eb0af69df0b 100644 --- a/mps/code/global.c +++ b/mps/code/global.c @@ -747,7 +747,7 @@ void (ArenaPoll)(Globals globals) /* fillMutatorSize has advanced; call TracePoll enough to catch up. */ start = ClockNow(); - EVENT3(ArenaPoll, arena, start, FALSE); + EVENT1(ArenaPollBegin, arena); do { moreWork = TracePoll(&tracedWork, &worldCollected, globals, @@ -762,7 +762,7 @@ void (ArenaPoll)(Globals globals) ArenaAccumulateTime(arena, start, ClockNow()); } - EVENT3(ArenaPoll, arena, start, BOOLOF(workWasDone)); + EVENT2(ArenaPollEnd, arena, BOOLOF(workWasDone)); globals->insidePoll = FALSE; } diff --git a/mps/tool/monitor b/mps/tool/monitor index ea7e21321be..bfb12e455ab 100755 --- a/mps/tool/monitor +++ b/mps/tool/monitor @@ -11,7 +11,7 @@ import argparse -from collections import deque, namedtuple +from collections import defaultdict, deque, namedtuple from itertools import cycle import os from struct import Struct @@ -189,6 +189,28 @@ class Accumulator(TimeSeries): self.append(t, self.value) +class MovingAverageRatio(TimeSeries): + "Exponentially weighted moving average on/off ratio." + def __init__(self, t, alpha=0.99): + super().__init__() + self._on = self._off = 0.0 + self._last = t + self._alpha = alpha + self._beta = 1 - alpha + self._ratio = 0.0 + + def off(self, t): + self._on = t - self._last + self._last = t + ratio = self._on / (self._on + self._off) + self._ratio = self._ratio * self._alpha + ratio * self._beta + self.append(t, self._ratio) + + def on(self, t): + self._off = t - self._last + self._last = t + + class EventHandler: """Object that handles a telemetry event by dispatching to the method with the same name as the event. @@ -204,8 +226,8 @@ class EventHandler: class Pool(EventHandler): "Model of an MPS pool." - def __init__(self, arena, pointer): - "Create Pool owned by arena, at pointer." + def __init__(self, arena, pointer, t): + "Create Pool owned by arena, at pointer, at time t." self._arena = arena # Owning arena. self._model = arena.model # Owning model. self._pointer = pointer # Pool's pointer. @@ -213,7 +235,7 @@ class Pool(EventHandler): self._serial = None # Pool's serial number within arena. self._alloc = Accumulator() self._model.add_time_series( - self, "alloc", self._alloc, + self, self._alloc, "bytes", "alloc", "memory allocated by the pool from the arena") @property @@ -240,14 +262,18 @@ class Pool(EventHandler): class Arena(EventHandler): "Model of an MPS arena." - def __init__(self, model, pointer): - "Create Arena owned by model, at pointer." + def __init__(self, model, pointer, t): + "Create Arena owned by model, at pointer, at time t." self.model = model # Owning model. self._pointer = pointer # Arena's pointer. self._arena_class = None # Arena's class pointer. self._serial = None # Arena's serial number. self._pools = [] # List of Pools ever belonging to arena. self._pool = {} # pointer -> Pool (for live pools) + self._poll = MovingAverageRatio(t) + self.model.add_time_series( + self, self._poll, "fraction", "poll", + "polling fraction of CPU time (moving average)") @property def name(self): @@ -269,7 +295,7 @@ class Arena(EventHandler): try: pool = self._pool[pointer] except KeyError: - self._pool[pointer] = pool = Pool(self, pointer) + self._pool[pointer] = pool = Pool(self, pointer, t) self._pools.append(pool) pool.handle(t, event) @@ -284,16 +310,23 @@ class Arena(EventHandler): def PoolFinish(self, t, event): del self._pool[event.pool] + def ArenaPollBegin(self, t, event): + self._poll.on(t) + + def ArenaPollEnd(self, t, event): + self._poll.off(t) + class Line: "A line in a Matplotlib plot wrapping a TimeSeries." colors = cycle('blue orange green red purple brown pink gray olive cyan' .split()) - def __init__(self, owner, name, series, desc): + def __init__(self, owner, series, unit, name, desc): self.owner = owner # Owning object. - self._name = name # Brief description. self.series = series # Time series. + self.unit = unit # Unit. + self._name = name # Brief description. self.desc = desc # Brief description. self.draw = True # Plot this line? self.color = next(self.colors) @@ -323,25 +356,33 @@ class Model(EventHandler): self._label = {} # address or pointer -> stringId self._arena = {} # pointer -> Arena (for live arenas) self.arenas = [] # All arenas created in the model. - self.lines = [] # All time series available for plotting. + self.lines = [] # All Lines available for plotting. + self._unit_lines = defaultdict(list) # Lines collated by unit. self._needs_redraw = True # Plot needs redrawing? - def add_time_series(self, owner, name, series, desc): + def add_time_series(self, *args): "Add a time series to the model." - self.lines.append(Line(owner, name, series, desc)) + line = Line(*args) + self.lines.append(line) + self._unit_lines[line.unit].append(line) def label(self, pointer): "Return string labelling address or pointer, or None if unlabelled." return self._intern.get(self._label.get(pointer)) - def plot(self, axes): + def plot(self, axes_list): "Draw time series on the given axes." - if self._needs_redraw: - self._needs_redraw = False + if not self._needs_redraw: + return + self._needs_redraw = False + # Determine which unit to plot on each axis. + lines_list = sorted(self._unit_lines.values(), key=len, reverse=True) + for axes in axes_list: axes.clear() axes.set_xlabel("time (seconds)") - axes.set_ylabel("bytes") - for line in self.lines: + for axes, lines in zip(axes_list, lines_list): + axes.set_ylabel(lines[0].unit) + for line in lines: line.plot(axes) axes.figure.canvas.draw() @@ -363,12 +404,12 @@ class Model(EventHandler): try: arena = self._arena[addr] except KeyError: - self._arena[addr] = arena = Arena(self, addr) + self._arena[addr] = arena = Arena(self, addr, t) self.arenas.append(arena) arena.handle(t, event) - ArenaCreateVM = ArenaCreateCL = ArenaAlloc = ArenaFree = PoolInit = \ - PoolFinish = delegate_to_arena + ArenaCreateVM = ArenaCreateCL = ArenaAlloc = ArenaFree = ArenaPollBegin = \ + ArenaPollEnd = PoolInit = PoolFinish = delegate_to_arena def EventClockSync(self, t, event): self.needs_redraw() @@ -441,6 +482,7 @@ class ApplicationWindow(QtWidgets.QMainWindow): # Matplot canvas and toolbar. canvas = FigureCanvas(Figure(figsize=(10, 6))) self._axes = canvas.figure.subplots() + self._axes2 = self._axes.twinx() main_layout.addWidget(canvas) self._toolbar = ApplicationToolbar(canvas, self) self.addToolBar(QtCore.Qt.BottomToolBarArea, self._toolbar) @@ -465,7 +507,7 @@ class ApplicationWindow(QtWidgets.QMainWindow): self._home_limits = None self._model.update() if not self._toolbar.paused: - self._model.plot(self._axes) + self._model.plot([self._axes, self._axes2]) self._home_limits = self._limits # Find new time series and create corresponding checkboxes.