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:
parent
6fd9abf9bd
commit
c3a7104eff
11 changed files with 182 additions and 61 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
117
mps/tool/monitor
117
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$
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue