diff --git a/awlsim-client b/awlsim-client index f76119dd1..3f493144c 100755 --- a/awlsim-client +++ b/awlsim-client @@ -96,6 +96,8 @@ def usage(): print("Actions to be performed on the server:") print(" -r|--runstate RUN/STOP Set the run state of the CPU.") print(" -S|--stats Fetch and display CPU statistics.") + print(" --meas-start Start instruction time measurements.") + print(" --meas-stop Stop instruction time measurements.") print(" --shutdown Shutdown the core server system.") print(" --reboot Reboot the core server system.") @@ -116,7 +118,7 @@ def main(): "hc:t:L:sP:r:S", [ "help", "connect=", "timeout=", "loglevel=", "ssh-tunnel", "ssh-passphrase=", "ssh-user=", "ssh-port=", "ssh-localport=", "ssh-exe=", - "runstate=", "stats", "shutdown", "reboot", ]) + "runstate=", "stats", "meas-start", "meas-stop", "shutdown", "reboot", ]) except getopt.GetoptError as e: printError(str(e)) usage() @@ -179,6 +181,10 @@ def main(): sys.exit(1) if o in ("-S", "--stats"): actions.append(("stats", None)) + if o == "--meas-start": + actions.append(("meas-start", None)) + if o == "--meas-stop": + actions.append(("meas-stop", None)) if o == "--shutdown": actions.append(("shutdown", None)) if o == "--reboot": @@ -219,6 +225,16 @@ def main(): client.setRunState(actionValue) elif action == "stats": printCpuStats(client.getCpuStats(sync=True)) + elif action == "meas-start": + if not client.measStart(): + printError("Failed to start measurements.") + elif action == "meas-stop": + reportData = client.measStop() + if reportData: + sys.stdout.write(reportData) + sys.stdout.flush() + else: + printError("Measurement failed. No data.") elif action == "shutdown": client.shutdownCoreServerSystem() elif action == "reboot": diff --git a/awlsim-test b/awlsim-test index c117fbfd3..f8c90dd79 100755 --- a/awlsim-test +++ b/awlsim-test @@ -305,7 +305,7 @@ def run(inputFile): if insnMeas: if insnMeas.haveAnyMeasurements: if opt_insnMeas == "-": - insnMeas.dump() + writeStdout(insnMeas.dump()) else: with open(opt_insnMeas, "wb") as fd: fd.write(insnMeas.dumpCSV().encode("UTF-8")) diff --git a/awlsim/core/cpu.py b/awlsim/core/cpu.py index 1d689d1fa..c16ce96a9 100644 --- a/awlsim/core/cpu.py +++ b/awlsim/core/cpu.py @@ -794,10 +794,15 @@ def reset(self): self.initializeTimestamp() - def setupInsnMeas(self): - if not self.__insnMeas: - self.__insnMeas = InsnMeas() - return self.__insnMeas + def setupInsnMeas(self, enable=True): + if enable: + if not self.__insnMeas: + self.__insnMeas = InsnMeas() + insnMeas = self.__insnMeas + else: + insnMeas = self.__insnMeas + self.__insnMeas = None + return insnMeas def setCycleExitCallback(self, cb, data=None): self.cbCycleExit = cb diff --git a/awlsim/core/insnmeas.py b/awlsim/core/insnmeas.py index 2b9a85735..a0ce738c3 100644 --- a/awlsim/core/insnmeas.py +++ b/awlsim/core/insnmeas.py @@ -127,11 +127,12 @@ def __allMeasData(self): def dump(self): if not self.haveAnyMeasurements: return - printInfo("") - printInfo("Instruction time measurements:") + ret = [] + ret.append("Instruction time measurements:") for insnType, measData in self.__allMeasData: name = AwlInsnTypes.type2name_german[insnType] - printInfo(measData.dump(name)) + ret.append(measData.dump(name)) + return "\n".join(ret) + "\n" def dumpCSV(self): if not self.haveAnyMeasurements: @@ -149,4 +150,4 @@ def dumpCSV(self): measData.minRt * 1.0e6, measData.maxRt * 1.0e6, measData.avgRt * 1.0e6)) - return "\n".join(ret) + return "\n".join(ret) + "\n" diff --git a/awlsim/coreclient/client.py b/awlsim/coreclient/client.py index ee0d8d3ea..b68301c1d 100644 --- a/awlsim/coreclient/client.py +++ b/awlsim/coreclient/client.py @@ -859,6 +859,45 @@ def getCpuStats(self, sync=False): self.__send(msg) return True + def measStart(self, sync=True): + """Start instruction time measurements. + """ + if not self.__transceiver: + return None + msg = AwlSimMessage_MEAS_CONFIG( + flags=AwlSimMessage_MEAS_CONFIG.FLG_ENABLE) + if sync: + rxMsg = self.__sendAndWait(msg, + lambda rxMsg: (rxMsg.msgId == AwlSimMessage.MSG_ID_MEAS, + rxMsg.isReplyTo(msg))) + if rxMsg.flags & AwlSimMessage_MEAS.FLG_FAIL: + return False + else: + self.__send(msg) + return True + + def measStop(self, csv=True, sync=True): + """Stop instruction time measurements and + return the measurement report data string, if any. + """ + if not self.__transceiver: + return None + msg = AwlSimMessage_MEAS_CONFIG( + flags=AwlSimMessage_MEAS_CONFIG.FLG_GETMEAS) + if csv: + msg.flags |= AwlSimMessage_MEAS_CONFIG.FLG_CSV + if sync: + rxMsg = self.__sendAndWait(msg, + lambda rxMsg: (rxMsg.msgId == AwlSimMessage.MSG_ID_MEAS, + rxMsg.isReplyTo(msg))) + if ((rxMsg.flags & AwlSimMessage_MEAS.FLG_FAIL) or + not (rxMsg.flags & AwlSimMessage_MEAS.FLG_HAVEDATA)): + return "" + return rxMsg.reportStr + else: + self.__send(msg) + return "" + def shutdownCoreServer(self): """Shut down the core server. """ diff --git a/awlsim/coreserver/messages.py b/awlsim/coreserver/messages.py index 59f84e3ad..0002b85be 100644 --- a/awlsim/coreserver/messages.py +++ b/awlsim/coreserver/messages.py @@ -149,6 +149,8 @@ class AwlSimMessage(object): MSG_ID_MEMORY = EnumGen.item MSG_ID_INSNSTATE_CONFIG = EnumGen.item MSG_ID_INSNSTATE = EnumGen.item + MSG_ID_MEAS_CONFIG = EnumGen.item + MSG_ID_MEAS = EnumGen.item EnumGen.end _bytesLenStruct = struct.Struct(str(">I")) @@ -1299,6 +1301,101 @@ def fromBytes(cls, payload): raise TransferError("INSNSTATE_CONFIG: Invalid data format") return cls(flags, sourceId, fromLine, toLine, ob1Div, userData) +class AwlSimMessage_MEAS(AwlSimMessage): + msgId = AwlSimMessage.MSG_ID_MEAS + + # Payload data struct: + # Flags (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # measurement report data (string) + plDataStruct = struct.Struct(str(">IIIIIIIIIIIIIIII")) + + # Flags: + FLG_HAVEDATA = 1 << 0 + FLG_FAIL = 1 << 1 + + def __init__(self, flags, reportStr): + self.flags = flags & 0xFFFFFFFF + self.reportStr = reportStr + + def toBytes(self): + pl = self.plDataStruct.pack(self.flags, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + pl += self.packString(self.reportStr) + return AwlSimMessage.toBytes(self, len(pl)) + pl + + @classmethod + def fromBytes(cls, payload): + try: + offset = 0 + flags, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _ =\ + cls.plDataStruct.unpack_from(payload, offset) + offset += cls.plDataStruct.size + reportStr, offset = cls.unpackString(payload, offset) + except (struct.error, IndexError) as e: + raise TransferError("MEAS: Invalid data format") + return cls(flags, reportStr) + +class AwlSimMessage_MEAS_CONFIG(AwlSimMessage): + msgId = AwlSimMessage.MSG_ID_MEAS_CONFIG + + # Payload data struct: + # Flags (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + # reserved (32 bit) + plDataStruct = struct.Struct(str(">IIIIIIIIIIIIIIII")) + + # Flags: + FLG_ENABLE = 1 << 0 + FLG_GETMEAS = 1 << 1 + FLG_CSV = 1 << 2 + + def __init__(self, flags): + self.flags = flags & 0xFFFFFFFF + + def toBytes(self): + pl = self.plDataStruct.pack( + self.flags, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + return AwlSimMessage.toBytes(self, len(pl)) + pl + + @classmethod + def fromBytes(cls, payload): + try: + flags, _, _, _,\ + _, _, _, _, _, _, _, _, _, _, _, _ =\ + cls.plDataStruct.unpack_from(payload, 0) + except (struct.error, IndexError) as e: + raise TransferError("MEAS_CONFIG: Invalid data format") + return cls(flags) + class AwlSimMessage_REMOVESRC(AwlSimMessage): msgId = AwlSimMessage.MSG_ID_REMOVESRC @@ -1674,10 +1771,12 @@ class AwlSimMessageTransceiver(object): AwlSimMessage.MSG_ID_CPUDUMP : AwlSimMessage_CPUDUMP, AwlSimMessage.MSG_ID_REQ_MEMORY : AwlSimMessage_REQ_MEMORY, AwlSimMessage.MSG_ID_MEMORY : AwlSimMessage_MEMORY, - AwlSimMessage.MSG_ID_INSNSTATE_CONFIG : AwlSimMessage_INSNSTATE_CONFIG, - AwlSimMessage.MSG_ID_INSNSTATE : AwlSimMessage_INSNSTATE, AwlSimMessage.MSG_ID_GET_CPUSTATS : AwlSimMessage_GET_CPUSTATS, AwlSimMessage.MSG_ID_CPUSTATS : AwlSimMessage_CPUSTATS, + AwlSimMessage.MSG_ID_INSNSTATE_CONFIG : AwlSimMessage_INSNSTATE_CONFIG, + AwlSimMessage.MSG_ID_INSNSTATE : AwlSimMessage_INSNSTATE, + AwlSimMessage.MSG_ID_MEAS_CONFIG : AwlSimMessage_MEAS_CONFIG, + AwlSimMessage.MSG_ID_MEAS : AwlSimMessage_MEAS, } DEFAULT_TX_BUF_SIZE = 1024 * 100 diff --git a/awlsim/coreserver/server.py b/awlsim/coreserver/server.py index 22b811923..07f11d040 100644 --- a/awlsim/coreserver/server.py +++ b/awlsim/coreserver/server.py @@ -1283,6 +1283,32 @@ def __rx_INSNSTATE_CONFIG(self, client, msg): if msg.flags & msg.FLG_SYNC: client.transceiver.send(AwlSimMessage_REPLY.make(msg, status)) + def __rx_MEAS_CONFIG(self, client, msg): + printDebug("Received message: MEAS_CONFIG") + replyFlags = 0 + replyStr = "" + insnMeas = None + if msg.flags & msg.FLG_ENABLE: + printDebug("Enabling instruction time measurements") + insnMeas = self.__sim.cpu.setupInsnMeas(True) + if not insnMeas: + replyFlags |= AwlSimMessage_MEAS.FLG_FAIL + else: + printDebug("Disabling instruction time measurements") + insnMeas = self.__sim.cpu.setupInsnMeas(False) + if msg.flags & msg.FLG_GETMEAS: + if insnMeas: + if msg.flags & msg.FLG_CSV: + replyStr = insnMeas.dumpCSV() + else: + replyStr = insnMeas.dump() + if replyStr: + replyFlags |= AwlSimMessage_MEAS.FLG_HAVEDATA + reply = AwlSimMessage_MEAS(flags=replyFlags, + reportStr=replyStr) + reply.setReplyTo(msg) + client.transceiver.send(reply) + def __rx_GET_IDENTS(self, client, msg): printDebug("Received message: GET_IDENTS") awlSrcs = symSrcs = hwMods = libSels = fupSrcs = kopSrcs = () @@ -1357,6 +1383,7 @@ def __rx_GET_CPUSTATS(self, client, msg): AwlSimMessage.MSG_ID_REQ_MEMORY : (__rx_REQ_MEMORY, RXFLG_NONE), AwlSimMessage.MSG_ID_MEMORY : (__rx_MEMORY, RXFLG_NONE), AwlSimMessage.MSG_ID_INSNSTATE_CONFIG : (__rx_INSNSTATE_CONFIG, RXFLG_NONE), + AwlSimMessage.MSG_ID_MEAS_CONFIG : (__rx_MEAS_CONFIG, RXFLG_NONE), AwlSimMessage.MSG_ID_GET_IDENTS : (__rx_GET_IDENTS, RXFLG_NONE), # AwlSimMessage.MSG_ID_GET_CPUDUMP : (__rx_GET_CPUDUMP, RXFLG_NONE), AwlSimMessage.MSG_ID_GET_CPUSTATS : (__rx_GET_CPUSTATS, RXFLG_NONE),