From f8502d4b487b48b255f50135f6fef98a8192a1d7 Mon Sep 17 00:00:00 2001 From: AlexRast Date: Fri, 19 Jul 2019 14:31:32 +0100 Subject: [PATCH 01/14] Added nameserver process --- Build/gcc/Makefile | 8 +- Build/gcc/Makefile.executable_prerequisites | 37 +- Source/Common/PMsg_p.cpp | 1 + Source/Common/PMsg_p.hpp | 1 + Source/Common/Pglobals.cpp | 45 +- Source/Common/Pglobals.h | 57 +- Source/Common/SDecode.cpp | 52 + Source/NameServer/ABDefs.cpp | 54 + Source/NameServer/ABDefs.hpp | 60 + Source/NameServer/ABRecord.cpp | 40 + Source/NameServer/ABRecord.hpp | 39 + Source/NameServer/ABTask.cpp | 802 +++++++++++ Source/NameServer/ABTask.hpp | 139 ++ Source/NameServer/AddressBook.cpp | 1352 +++++++++++++++++++ Source/NameServer/AddressBook.hpp | 162 +++ Source/NameServer/NameServer.cpp | 174 ++- Source/NameServer/NameServer.h | 36 +- Source/NameServer/SBase.cpp | 726 ++++++++++ Source/NameServer/SBase.h | 76 ++ Source/OrchBase/OrchBase.cpp | 5 + Source/OrchBase/OrchBaseLink.cpp | 171 ++- Source/OrchBase/OrchBaseTask.cpp | 219 ++- Source/OrchBase/P_super.cpp | 5 + Source/OrchBase/P_task.cpp | 1 + Source/OrchBase/P_task.h | 2 + Source/OrchBase/build_defs.cpp | 2 +- Source/OrchBase/build_defs.h | 3 +- Source/OrchestratorMessages.txt | 25 + 28 files changed, 4191 insertions(+), 103 deletions(-) create mode 100644 Source/Common/SDecode.cpp create mode 100644 Source/NameServer/ABDefs.cpp create mode 100644 Source/NameServer/ABDefs.hpp create mode 100644 Source/NameServer/ABRecord.cpp create mode 100644 Source/NameServer/ABRecord.hpp create mode 100644 Source/NameServer/ABTask.cpp create mode 100644 Source/NameServer/ABTask.hpp create mode 100644 Source/NameServer/AddressBook.cpp create mode 100644 Source/NameServer/AddressBook.hpp create mode 100644 Source/NameServer/SBase.cpp create mode 100644 Source/NameServer/SBase.h diff --git a/Build/gcc/Makefile b/Build/gcc/Makefile index e4b83dcb..3d77a004 100644 --- a/Build/gcc/Makefile +++ b/Build/gcc/Makefile @@ -105,16 +105,18 @@ ROOT_EXECUTABLE = $(EXECUTABLE_DIR)/root DUMMY_EXECUTABLE = $(EXECUTABLE_DIR)/dummy LOGSERVER_EXECUTABLE = $(EXECUTABLE_DIR)/logserver LOGSERVER_MESSAGE_FILE = $(EXECUTABLE_DIR)/OrchestratorMessages.txt +NAMESERVER_EXECUTABLE = $(EXECUTABLE_DIR)/nameserver RTCL_EXECUTABLE = $(EXECUTABLE_DIR)/rtcl INJECTOR_EXECUTABLE = $(EXECUTABLE_DIR)/injector MOTHERSHIP_EXECUTABLE = $(EXECUTABLE_DIR)/mothership -all: orchestrate root dummy logserver rtcl injector mothership \ +all: orchestrate root dummy logserver nameserver rtcl injector mothership \ application_staging_environment orchestrate: $(ORCHESTRATE_SCRIPT) root: $(ROOT_EXECUTABLE) dummy: $(DUMMY_EXECUTABLE) logserver: $(LOGSERVER_EXECUTABLE) $(LOGSERVER_MESSAGE_FILE) +nameserver: $(NAMESERVER_EXECUTABLE) rtcl: $(RTCL_EXECUTABLE) injector: $(INJECTOR_EXECUTABLE) mothership: $(MOTHERSHIP_EXECUTABLE) @@ -133,7 +135,7 @@ tests: $(ALL_TESTS) $(TEST_SCRIPT) # Makefile.executable_prerequisites for the prerequisites of these executables # (obviously...) $(ROOT_EXECUTABLE) $(DUMMY_EXECUTABLE) $(LOGSERVER_EXECUTABLE) \ -$(RTCL_EXECUTABLE) $(INJECTOR_EXECUTABLE): +$(NAMESERVER_EXECUTABLE) $(RTCL_EXECUTABLE) $(INJECTOR_EXECUTABLE): @$(shell $(MKDIR) $(EXECUTABLE_DIR)) $(CXX) -pthread -Wl,-rpath-link=$(QT_LIB_DIR) \ -L$(MPICH_LIB_DIR) -L$(QT_LIB_DIR) -L/usr/lib \ @@ -282,7 +284,7 @@ clean: application_staging_environment_teardown .PRECIOUS: $(DEPENDENCY_DIR)/%.d # Non-builtin targets that do not explicitly represent files that are created. -.PHONY: all clean debug tests orchestrate root dummy logserver rtcl injector \ +.PHONY: all clean debug tests orchestrate root dummy logserver nameserver rtcl injector \ mothership application_staging_environment \ application_staging_environment_teardown diff --git a/Build/gcc/Makefile.executable_prerequisites b/Build/gcc/Makefile.executable_prerequisites index 2790037f..0ceeb8e9 100644 --- a/Build/gcc/Makefile.executable_prerequisites +++ b/Build/gcc/Makefile.executable_prerequisites @@ -75,6 +75,9 @@ HARDWARE_FILE_PARSER_SOURCES = $(SOURCE_DIR)/OrchBase/HardwareFileManager/Hardwa $(GENERICS_DIR)/uif.cpp \ $(HARDWARE_DEPLOYER_SOURCES) +# The LogServer message file is a static text file +LOGSERVER_MESSAGE_FILE_ORIGIN = $(SOURCE_DIR)/OrchestratorMessages.txt + # The orchestrate execution script uses a template. ORCHESTRATE_TEMPLATE = ./Resources/orchestrate_template.sh @@ -102,7 +105,11 @@ ROOT_SOURCES = $(SOURCE_DIR)/Root/RootMain.cpp \ $(GENERICS_DIR)/filename.cpp \ $(GENERICS_DIR)/lex.cpp \ $(GENERICS_DIR)/rand.cpp \ - $(SOURCE_DIR)/NameServer/Ns_el.cpp + $(SOURCE_DIR)/NameServer/Ns_el.cpp \ + $(SOURCE_DIR)/NameServer/ABDefs.cpp \ + $(SOURCE_DIR)/NameServer/ABRecord.cpp \ + $(SOURCE_DIR)/NameServer/ABTask.cpp \ + $(SOURCE_DIR)/NameServer/AddressBook.cpp \ ROOT_SOURCES += $(TRULY_COMMON_SOURCES) ROOT_SOURCES += $(HARDWARE_FILE_PARSER_SOURCES) @@ -151,9 +158,27 @@ LOGSERVER_SOURCES = $(SOURCE_DIR)/LogServer/LogServerMain.cpp \ LOGSERVER_SOURCES += $(TRULY_COMMON_SOURCES) -# The logserver also needs OrchestratorMessages.txt to exist in the directory -# from which the Orchestrator is started. -LOGSERVER_MESSAGE_FILE_ORIGIN = $(SOURCE_DIR)/OrchestratorMessages.txt +# The nameserver component consists of: +# +# - The "main" file (/Source/NameServer/NameServerMain.cpp) and its dependencies +# in /Source/NameServer. +# - TODO: Migrate SBase and AddressBook into a common directory and source group +# +# - The truly common sources. +# +# - The hardware and software address sources. +# +NAMESERVER_SOURCES = $(SOURCE_DIR)/NameServer/NameServerMain.cpp \ + $(SOURCE_DIR)/NameServer/NameServer.cpp \ + $(SOURCE_DIR)/NameServer/ABDefs.cpp \ + $(SOURCE_DIR)/NameServer/ABRecord.cpp \ + $(SOURCE_DIR)/NameServer/ABTask.cpp \ + $(SOURCE_DIR)/NameServer/AddressBook.cpp \ + $(SOURCE_DIR)/NameServer/SBase.cpp + +NAMESERVER_SOURCES += $(TRULY_COMMON_SOURCES) +#NAMESERVER_SOURCES += $(SOFTWARE_ADDRESS_SOURCES) +#NAMESERVER_SOURCES += $(HARDWARE_ADDRESS_SOURCES) # The rtcl (real-time clock) component consists of: # @@ -256,7 +281,7 @@ $(1)_OBJECTS := $(call orch_sources_to_objects, $($(1)_SOURCES)) endef $(foreach object_set,\ - ROOT INJECTOR DUMMY LOGSERVER RTCL MOTHERSHIP,\ + ROOT INJECTOR DUMMY LOGSERVER NAMESERVER RTCL MOTHERSHIP,\ $(eval $(call OBJECT_TEMPLATE,$(object_set)))) # Define executable prerequisites. RULE_TEMPLATE defines the substitutions made @@ -269,5 +294,5 @@ $($(1)_EXECUTABLE): $($(1)_OBJECTS) endef $(foreach executable_name,\ - ROOT DUMMY LOGSERVER RTCL INJECTOR MOTHERSHIP,\ + ROOT DUMMY LOGSERVER NAMESERVER RTCL INJECTOR MOTHERSHIP,\ $(eval $(call RULE_TEMPLATE,$(executable_name)))) diff --git a/Source/Common/PMsg_p.cpp b/Source/Common/PMsg_p.cpp index d7b4ca19..810b2427 100644 --- a/Source/Common/PMsg_p.cpp +++ b/Source/Common/PMsg_p.cpp @@ -12,6 +12,7 @@ and when I come across a need. PMsg_p::PMsg_p(MPI_Comm c):Msg_p(){comm=c;} PMsg_p::PMsg_p(byte * pb,int l,MPI_Comm c):Msg_p(pb,l){comm=c;} +PMsg_p::PMsg_p(Msg_p & m, MPI_Comm c):Msg_p(m){comm=c;}; PMsg_p::PMsg_p(PMsg_p & r):Msg_p(r){comm=r.comm;} PMsg_p::~PMsg_p(void){} diff --git a/Source/Common/PMsg_p.hpp b/Source/Common/PMsg_p.hpp index a1aa431c..8a271706 100644 --- a/Source/Common/PMsg_p.hpp +++ b/Source/Common/PMsg_p.hpp @@ -10,6 +10,7 @@ class PMsg_p : public Msg_p { public : PMsg_p(MPI_Comm c=MPI_COMM_NULL); PMsg_p(byte * pb,int l,MPI_Comm c=MPI_COMM_NULL); + PMsg_p(Msg_p & m, MPI_Comm c=MPI_COMM_NULL); PMsg_p(PMsg_p & r); virtual ~ PMsg_p(void); void Bcast(); diff --git a/Source/Common/Pglobals.cpp b/Source/Common/Pglobals.cpp index 10ecef0d..7761461d 100644 --- a/Source/Common/Pglobals.cpp +++ b/Source/Common/Pglobals.cpp @@ -15,6 +15,7 @@ const byte Q::INJCT; const byte Q::NAME; const byte Q::SUPR; const byte Q::TINS; +const byte Q::CANDC; // Level 1 subkeys const byte Q::PING; const byte Q::POST; @@ -28,10 +29,16 @@ const byte Q::RUN; const byte Q::LOAD; const byte Q::STOP; const byte Q::TOPO; -const byte Q::DIST; -const byte Q::TDIR; const byte Q::SHOW; const byte Q::ACPT; +// Nameserver additions ---------------------------------------------------- +const byte Q::SEND; +const byte Q::QRY; +const byte Q::RPLY; +const byte Q::DATA; +const byte Q::CFG; +const byte Q::CMDC; +const byte Q::DUMP; // temporary use: for MPI testing ------------------------------------------ const byte Q::M0; const byte Q::M1; @@ -41,7 +48,41 @@ const byte Q::MN; const byte Q::REQ; const byte Q::ACK; const byte Q::FWD; +// Nameserver additions --------------------------------------------------- +const byte Q::TASK; +const byte Q::DEVT; +const byte Q::DEVI; +const byte Q::SUPV; +const byte Q::EXTN; +const byte Q::LIST; +const byte Q::BLD; +const byte Q::INTG; +const byte Q::STATE; +// moved from L1 to L2 for Nameserver -------------------------------------- +const byte Q::DIST; +const byte Q::RECL; +const byte Q::TDIR; +const byte Q::DEL; +const byte Q::MONI; +const byte Q::LOGN; +const byte Q::DEVE; // Level 3 subkeys +const byte Q::FALSE; +const byte Q::TRUE; +const byte Q::OFF; +const byte Q::ON; +const byte Q::NF; +const byte Q::TNF; +const byte Q::NM; +const byte Q::ID; +const byte Q::ALL; +const byte Q::NGRP; +const byte Q::IGRP; +const byte Q::NSUP; +const byte Q::ISUP; +const byte Q::IN; +const byte Q::OUT; +const byte Q::ATR; // Not a value const byte Q::NAV; diff --git a/Source/Common/Pglobals.h b/Source/Common/Pglobals.h index a99e1cbc..dde7c3db 100644 --- a/Source/Common/Pglobals.h +++ b/Source/Common/Pglobals.h @@ -127,22 +127,63 @@ static const byte RUN = 0x48; static const byte LOAD = 0x49; static const byte STOP = 0x4a; static const byte TOPO = 0x4b; -static const byte DIST = 0x4c; -static const byte RECL = 0x4d; -static const byte TDIR = 0x4e; -static const byte SHOW = 0x4f; -static const byte ACPT = 0x50; +static const byte SHOW = 0x4c; +static const byte ACPT = 0x4d; +// Nameserver additions ---------------------------------------------------- +static const byte SEND = 0x4e; +static const byte QRY = 0x4f; +static const byte RPLY = 0x50; +static const byte DATA = 0x51; +static const byte CFG = 0x52; +static const byte CMDC = 0x53; +static const byte DUMP = 0x54; // temporary use: for MPI testing ------------------------------------------ static const byte M0 = 0x60; static const byte M1 = 0x61; static const byte MN = 0x62; -//-------------------------------------------------------------------------- +// _________________________________________________________________________ // Level 2 subkeys static const byte REQ = 0x80; static const byte ACK = 0x81; static const byte FWD = 0x82; +// Nameserver additions --------------------------------------------------- +static const byte TASK = 0x83; +static const byte DEVT = 0x84; +static const byte DEVI = 0x85; +static const byte SUPV = 0x86; +static const byte EXTN = 0x87; +static const byte ATTR = 0x88; +static const byte LIST = 0x89; +static const byte BLD = 0x8a; +static const byte INTG = 0x8b; +// moved from L1 to L2 for Nameserver -------------------------------------- +static const byte DIST = 0x8c; +static const byte RECL = 0x8d; +static const byte TDIR = 0x8e; +static const byte STATE = 0x8f; +static const byte DEL = 0x90; +static const byte MONI = 0x91; +static const byte LOGN = 0x92; +static const byte DEVE = 0x93; +// _________________________________________________________________________ // Level 3 subkeys - +static const byte FALSE = 0xc0; +static const byte TRUE = 0xc1; +static const byte OFF = 0xc2; +static const byte ON = 0xc3; +static const byte NF = 0xc4; +static const byte TNF = 0xc5; +static const byte NM = 0xc6; +static const byte ID = 0xc7; +static const byte ALL = 0xc8; +static const byte NGRP = 0xc9; +static const byte IGRP = 0xca; +static const byte NSUP = 0xcb; +static const byte ISUP = 0xcc; +static const byte IN = 0xcd; +static const byte OUT = 0xce; +static const byte ATR = 0xcf; + // Not a value static const byte NAV = 0xff; static const int NAP = -1; @@ -156,7 +197,7 @@ static const byte ROOT = 0x00; #define csLOGSERVERproc "LogServer:CommonBase" #define csRTCLproc "RTCL:CommonBase" #define csINJECTORproc "Injector:CommonBase" -#define csNAMESERVERproc "NameServer:CommonBase" +#define csNAMESERVERproc "NameServer:SBase:CommonBase" #define csMONITORproc "Monitor:CommonBase" #define csMOTHERSHIPproc "TMoth:CommonBase" #define csMPITESTproc "MPITest:CommonBase" diff --git a/Source/Common/SDecode.cpp b/Source/Common/SDecode.cpp new file mode 100644 index 00000000..d2713fce --- /dev/null +++ b/Source/Common/SDecode.cpp @@ -0,0 +1,52 @@ + +/* The reason for the rather bizarre way of defining this method - via the +#include in the .h file - is because it's part of several derived classes, and +FnMapx and this are different in each derived class, and I can't find a way +of doing this without breaking syntax. Rather, I can and have, and this is it, +but I really, really don't like it. + +Perfection is the enemy of progress, and all that. This works. + +DON'T PUT ANY #include IN THIS FILE + +It's virtual here because this makes it an invalid translation unit, so if you +try to compile it, the compiler will squeak. +*/ + +//============================================================================== + +virtual unsigned Decode(PMsg_p * pPkt, unsigned cIdx) +// Routine to decode - that is, send to the correct handler - incoming messages +// Look at the message key, and use the map to call the appropriate (derived +// class) member. If it's not in the derived class map, try the base class map. +// If it's not there either, it gets junked. +{ +// printf("%s::Decode ",Sderived.c_str()); + // Handler in the derived class? +if (FnMapx[cIdx]->find(pPkt->Key())!=FnMapx[cIdx]->end()) { + // printf(".. derived, key 0x%x\n", pPkt->Key()); fflush(stdout); + return (this->*(*FnMapx[cIdx])[pPkt->Key()])(pPkt,cIdx); +} + // No. In SBase? +if (SBase::FnMapx[cIdx]->find(pPkt->Key())!=SBase::FnMapx[cIdx]->end()) { + // printf(".. sbase, key 0x%x\n", pPkt->Key()); fflush(stdout); + return (this->*(*SBase::FnMapx[cIdx])[pPkt->Key()])(pPkt,cIdx); +} + // Nope. Base class? +if (CommonBase::FnMapx[cIdx]->find(pPkt->Key())!=CommonBase::FnMapx[cIdx]->end()) { + // printf(".. base, key 0x%x\n", pPkt->Key()); fflush(stdout); + return (this->*(*CommonBase::FnMapx[cIdx])[pPkt->Key()])(pPkt,cIdx); +} +// printf(".. dropped, key 0x%x\n", pPkt->Key()); fflush(stdout); + // Nope. Kick. + // Pull out the unknown key and post what + // little we know to the LogServer +Post(101,Sderived,int2str(pPkt->Src()),pPmap[cIdx]->vPmap[pPkt->Src()].P_class,int2str(pPkt->Tgt()), + pPmap[cIdx]->vPmap[pPkt->Tgt()].P_class,hex2str(pPkt->Key())); +return 0; // Return "keep going" value +} + +//============================================================================== + + + diff --git a/Source/NameServer/ABDefs.cpp b/Source/NameServer/ABDefs.cpp new file mode 100644 index 00000000..48c492f8 --- /dev/null +++ b/Source/NameServer/ABDefs.cpp @@ -0,0 +1,54 @@ +#include "ABDefs.hpp" + +namespace AddressBookNS +{ + +// const SymAddr_t INVALID_ADDRESS; + +int DeviceData_t::size() const +{ + int size = 0; + + size += sizeof(SymAddr_t) * 2; + size += Name.size(); + + return size; +} + + +DevTypeRecord_t::DevTypeRecord_t(std::string N) +{ + Name = N; +} + +int DevTypeRecord_t::size() const +{ + int size = 0; + + size += Name.size(); + size += sizeof(MsgIdx) * InMsgs.size(); + size += sizeof(MsgIdx) * OuMsgs.size(); + + return size; +} + + +MsgTypeRecord_t::MsgTypeRecord_t(std::string N) +{ + Name = N; +} + +int MsgTypeRecord_t::size() const +{ + int size = 0; + + size += Name.size(); + size += sizeof(MsgIdx) * Inputs.size(); + size += sizeof(MsgIdx) * Outputs.size(); + + return size; +} + + + +} /* namespace AddressBookNS */ diff --git a/Source/NameServer/ABDefs.hpp b/Source/NameServer/ABDefs.hpp new file mode 100644 index 00000000..15e7e9dc --- /dev/null +++ b/Source/NameServer/ABDefs.hpp @@ -0,0 +1,60 @@ +#ifndef ABDefs_H +#define ABDefs_H + +#include "OSFixes.hpp" + +#include +#include +#include +#include + +namespace AddressBookNS +{ + +//---------------------------------------- +// Temporary typedefs +typedef uint64_t SymAddr_t; // A 64-bit full-symbolic address + +static const SymAddr_t INVALID_ADDRESS = UINT64_MAX; +//typedef unsigned long SymAddr_t; // A 64-bit full-symbolic address + +//---------------------------------------- + +enum TaskState_t { Loaded = 0, Linked, Built, Deployed, Init, Running, Finished, Unknown }; + +typedef unsigned DTypeIdx; +typedef int AttrIdx; +typedef unsigned MsgIdx; + + +struct DeviceData_t { + int size() const; // Get the size of the DeviceData + + SymAddr_t Address; // Device's full sym address. + SymAddr_t Supervisor; // Full sym address of the Device's Supervisor. + std::string Name; // Device's cnonical name. +}; + + +struct DevTypeRecord_t { + DevTypeRecord_t(std::string N = ""); + int size() const; // Get the size of the DeviceType Record + + std::string Name; // Device Type cnonical name. + std::vector InMsgs; // Vector of Input Message indexes + std::vector OuMsgs; // Vector of Output Message indexes +}; + + + +struct MsgTypeRecord_t { + MsgTypeRecord_t(std::string N = ""); + int size() const; // Get the size of the MsgType Record + + std::string Name; // Message Type Name + std::vector Inputs; // Indicies of DeviceTypes that have this MessageType as an input + std::vector Outputs; // Indicies of DeviceTypes that have this MessageType as an output +}; + +} /* namespace AddressBookNS */ +#endif /* ABDefs_H */ diff --git a/Source/NameServer/ABRecord.cpp b/Source/NameServer/ABRecord.cpp new file mode 100644 index 00000000..1fb2e383 --- /dev/null +++ b/Source/NameServer/ABRecord.cpp @@ -0,0 +1,40 @@ +#include "ABRecord.hpp" + +namespace AddressBookNS +{ + +Record_t::Record_t() +{ + Attribute = -1; +} + +Record_t::Record_t(std::string Name, SymAddr_t Addr, uint64_t Super, + DTypeIdx Type, RecordType_t RT, AttrIdx Attr) + : Name(Name) +{ + Address = Addr; + DeviceType = Type; + RecordType = RT; + Attribute = Attr; + Rank = 0; + Supervisor = 0; + if (RecordType == Supervisor) Rank = static_cast(Super); + else Supervisor = static_cast(Super); +} + + +int Record_t::size() const +{ + int size = 0; + + size += sizeof(SymAddr_t) * 2; // Address and Supervisor/Rank + size += sizeof(DTypeIdx); // Device Type Idx + size += sizeof(AttrIdx); // Attribute Idx + size += sizeof(RecordType_t); // Record Type + size += sizeof(char) * Name.size(); // Name + + return size; +} + + +} /* namespace AddressBookNS */ diff --git a/Source/NameServer/ABRecord.hpp b/Source/NameServer/ABRecord.hpp new file mode 100644 index 00000000..ef011887 --- /dev/null +++ b/Source/NameServer/ABRecord.hpp @@ -0,0 +1,39 @@ +#ifndef ABRecord_H +#define ABRecord_H + +#include "ABDefs.hpp" + +namespace AddressBookNS +{ + enum RecordType_t { Device = 0, DeviceExt, External, Supervisor }; + + // data contents factored out for serialisation 17 July 2019 ADR + struct RecordData_t + { + public: + + SymAddr_t Address; // Device's full sym address. + union + { + SymAddr_t Supervisor; // Full sym address of the Device's Supervisor. + unsigned long Rank; // OR Supervisor's MPI Rank. + }; + DTypeIdx DeviceType; // Index of the Device's type in task. + AttrIdx Attribute; // Index of the Device's attribute in task. + RecordType_t RecordType; // Class of device represented by the record. + }; + + class Record_t : public RecordData_t + { + public: + + Record_t(); + Record_t(std::string, SymAddr_t, uint64_t, DTypeIdx, RecordType_t = Device, AttrIdx = -1); + + int size() const; // Size of the record + + std::string Name; // Device's canonical name. + }; + +} /* namespace AddressBookNS */ +#endif /* ABRecord_H */ diff --git a/Source/NameServer/ABTask.cpp b/Source/NameServer/ABTask.cpp new file mode 100644 index 00000000..57706876 --- /dev/null +++ b/Source/NameServer/ABTask.cpp @@ -0,0 +1,802 @@ +#include "ABTask.hpp" + +namespace AddressBookNS +{ + +/*============================================================================== + * TaskData + *============================================================================*/ +TaskData_t::TaskData_t() +{ + Name = ""; + Path = ""; + XML = ""; + ExecutablePath = ""; + State = Loaded; + DeviceCount = 0; + DeviceCountLd = 0; + ExternalCount = 0; + ExternalCountLd = 0; + SupervisorCount = 0; + + State = Loaded; +} + +TaskData_t::TaskData_t(std::string &N, std::string &P, std::string &X, std::string &E, + TaskState_t S, unsigned long DC, unsigned long EC) +{ + Name = N; + Path = P; + XML = X; + ExecutablePath = E; + State = Loaded; + DeviceCount = DC; + DeviceCountLd = 0; + ExternalCount = EC; + ExternalCountLd = 0; + SupervisorCount = 0; + + State = S; +} + +int TaskData_t::size() const +{ + int size = 0; + + size += sizeof(unsigned long)*5; // Size of the 5 longs + size += sizeof(char) * Name.size(); // Size of the name + size += sizeof(char) * Path.size(); // Size of the path + size += sizeof(char) * XML.size(); // Size of the XML Filename + size += sizeof(char) * ExecutablePath.size(); // Size of the exec path + size += sizeof(TaskState_t); + + for(std::vector::const_iterator DT = DeviceTypes.begin(); + DT != DeviceTypes.end(); DT++) // Size of the device types + { + size += DT->size(); + } + + for(std::vector::const_iterator MT = MessageTypes.begin(); + MT != MessageTypes.end(); MT++) // Size of the Message Types + { + size += MT->size(); + } + + for(std::vector::const_iterator AT = AttributeTypes.begin(); + AT != AttributeTypes.end(); AT++) // Size of the Attribute Types + { + size += AT->size(); + } + + return size; +} + +/*============================================================================== + * TaskRecord + *============================================================================*/ +TaskRecord_t::TaskRecord_t() +{ + Name = ""; + Path = ""; + XML = ""; + ExecPath = ""; + DevCntMax = 0; + ExtCntMax = 0; + DevCnt = 0; + SupCnt = 0; + ExtCnt = 0; + + State = Loaded; + + TaskValid = true; + MapValid = true; + LinkValid = true; +} + +TaskRecord_t::TaskRecord_t(std::string &N, std::string &P, std::string &X, std::string &E, + TaskState_t S, unsigned long DC, unsigned long EC) +{ + Name = N; + Path = P; + XML = X; + ExecPath = E; + DevCntMax = DC; + ExtCntMax = EC; + DevCnt = 0; + SupCnt = 0; + ExtCnt = 0; + + State = S; + + TaskValid = true; + MapValid = true; + LinkValid = true; +} + +unsigned TaskRecord_t::Integrity(bool Verbose, FILE * fp) +{ + ABULONG ExtConCnt(0); + ABUNSIGNED MappedSupervisors(0); + + IntegVals_t retVal; + retVal.ret = 0; + retVal.retT = 0; + retVal.retL = 0; + retVal.retM = 0; + + //fprintf(fp,"Task: %s\n", Search->first.c_str()); + + if (Verbose) fprintf(fp, "Integrity check of %s:\n", Name.c_str()); + + //========================================================================== + // Task checks + + // Device Counts - failing these means the task is broken? + if(Devices.size() != DevCnt) + { + // Device Count mismatch. + if (Verbose) fprintf(fp, "Device Count mismatch: %lu!=%lu\n", + static_cast(Devices.size()), DevCnt); + retVal.ret++; + } + + if(Externals.size() != ExtCnt) + { + // External Count mismatch. + if (Verbose) fprintf(fp, "External Count mismatch: %lu!=%lu\n", + static_cast(Externals.size()), ExtCnt); + retVal.ret++; + } + + if(Supervisors.size() != SupCnt) + { + // Supervisor Count mismatch. + if (Verbose) fprintf(fp, "Supervisor Count mismatch: %lu!=%lu\n", + static_cast(Supervisors.size()), SupCnt); + retVal.ret++; + } + + if(SupMap.size() != SupCnt) + { + // Supervisor Count-SupMap size mismatch. + if (Verbose) fprintf(fp, "Supervisor Count<>SupMap size mismatch: %lu!=%lu\n", + static_cast(SupMap.size()), SupCnt); + retVal.ret++; + } + + +#ifdef AB_THREADING + //========================================================================== + // Multi-threaded checks of large structures. Number of threads used scales + // with the number of CPU cores. The checks are read-only so no locks for + // data structure access. Atomic types are used for return values. + unsigned threadCnt = 0, i = 0; + + // Base the scaling off of the available concurrency, with a minimum of 1 + // thread for each check, for a minimum of 4 total threads. Otherwise, + // maximum concurrency is 2* available concurrency. + unsigned int concurrency = std::thread::hardware_concurrency(); + unsigned int halfConcurrency = (concurrency < 2) ? 1 : concurrency / 2; + unsigned int qrtrConcurrency = (concurrency < 4) ? 1 : concurrency / 4; + + // Create a vector of threads of the correct size. + std::vector threads(concurrency + halfConcurrency + + qrtrConcurrency + qrtrConcurrency); + + + //========================================================================== + // Thread(s) for checking DeviceType vectors, etc. + unsigned long dtConcurrency = halfConcurrency; + if (DevTypes.size() < halfConcurrency) // Limit number of threads if we + { // have fewer DeviceTypes than + dtConcurrency = DevTypes.size(); // possible threads. + } + unsigned long devTypeSplit = DevTypes.size() / dtConcurrency; + for(i = 0; i < dtConcurrency; i++) + { + // Handle any remainder in the last thread - integ method checks bounds. + unsigned endMul = (i==(dtConcurrency-1)) ? 2 : 1; + threads[threadCnt+i] = std::thread(&TaskRecord_t::IntegDevTypeMap, this, + Verbose, fp, (devTypeSplit * i), + (devTypeSplit * (i+endMul)), + std::ref(retVal)); + } + threadCnt += i; + + //========================================================================== + // Thread(s) for checking Devices vector + unsigned long dConcurrency = concurrency; + if (Devices.size() < concurrency) // Limit number of threads if we + { // have fewer Devices than + dConcurrency = Devices.size(); // possible threads. + } + unsigned long long devSplit = Devices.size() / dConcurrency; + for(i = 0; i < dConcurrency; i++) + { + // Handle any remainder in the last thread - integ method checks bounds. + unsigned endMul = (i==(dConcurrency-1)) ? 2 : 1; + threads[threadCnt+i] = std::thread(&TaskRecord_t::IntegDevices, this, + Verbose, fp, (devSplit * i), + (devSplit * (i+endMul)), + std::ref(retVal), + std::ref(ExtConCnt)); + } + threadCnt += i; + + //========================================================================== + // Thread(s) for checking Externals vector + unsigned long eConcurrency = qrtrConcurrency; + if (Externals.size() < qrtrConcurrency) // Limit number of threads if we + { // have fewer Externals than + eConcurrency = Externals.size(); // possible threads. + } + unsigned long extSplit = Externals.size() / eConcurrency; + for(i = 0; i < eConcurrency; i++) + { + // Handle any remainder in the last thread - integ method checks bounds. + unsigned endMul = (i==(eConcurrency-1)) ? 2 : 1; + threads[threadCnt+i] = std::thread(&TaskRecord_t::IntegExternals, this, + Verbose, fp, (extSplit * i), + (extSplit * (i+endMul)), + std::ref(retVal)); + } + threadCnt += i; + + //========================================================================== + // Thread(s) for checking Supervisors vector. + unsigned long sConcurrency = qrtrConcurrency; + if (Supervisors.size() < qrtrConcurrency) // Limit number of threads if we + { // have fewer Supervisors than + sConcurrency = Supervisors.size(); // possible threads. + } + unsigned long superSplit = Supervisors.size() / sConcurrency; + for(i = 0; + i < sConcurrency; + i++) + { + // Handle any remainder in the last thread - integ method checks bounds. + unsigned endMul = (i==(sConcurrency-1)) ? 2 : 1; + threads[threadCnt+i] = std::thread(&TaskRecord_t::IntegSupervisors, this, + Verbose, fp, (superSplit * i), + (superSplit * (i+endMul)), + std::ref(retVal), + std::ref(MappedSupervisors)); + } + threadCnt += i; + + +#endif + + // Check integrity of MsgType map + for(std::vector::iterator M + =MsgTypes.begin();M!=MsgTypes.end();M++) // Iterate through message type vector + { + unsigned Idx = static_cast(std::distance(MsgTypes.begin(), M)); // Current Index + + IdxMap_t::const_iterator MSearch = MsgTypeMap.find(M->Name); // Find the Msg Name in the map + if (MSearch == MsgTypeMap.end()) { + // Message Type not mapped + if (Verbose) fprintf(fp, "T: Message Type not mapped: %s\n", + M->Name.c_str()); + retVal.retT++; + } else { + if (MsgTypes[MSearch->second].Name != M->Name){ + // Message Type > Index map is broken. + if (Verbose) fprintf(fp, "T: Message Type > Index map is broken: %s\n", + M->Name.c_str()); + retVal.retT++; + } + + if (Idx != MSearch->second) { + // Index mismatch + if (Verbose) fprintf(fp, "T: Index mismatch: %s, %u!=%u\n", + M->Name.c_str(), Idx, MSearch->second); + retVal.retT++; + } + } + } + + // Check integrity of AttributeType map + for(std::vector::iterator A + =AttrTypes.begin();A!=AttrTypes.end();A++) // Iterate through message type vector + { + unsigned Idx = static_cast(std::distance(AttrTypes.begin(), A)); // Current Index + + IdxMap_t::const_iterator ASearch = AttrTypeMap.find(A->first); // Find the Msg Name in the map + if (ASearch == AttrTypeMap.end()) { + // Attribute Type not mapped + if (Verbose) fprintf(fp, "T: Attribute Type not mapped: %s\n", + A->first.c_str()); + retVal.retT++; + } else { + if (AttrTypes[ASearch->second].first != A->first){ + // Attribute Type > Index map is broken. + if (Verbose) fprintf(fp, "T: Attribute > Index map broken: %s!=%s\n", + A->first.c_str(), AttrTypes[ASearch->second].first.c_str()); + retVal.retT++; + } + + if (Idx != ASearch->second) { + // Index mismatch + if (Verbose) fprintf(fp, "T: Attribute Index mismatch: %u!m%u\n", + Idx, ASearch->second); + retVal.retT++; + } + } + } + + + //========================================================================== + // Map checks + + // Map size integrity checks - Re-mapping should fix. + unsigned long totalDevCnt = DevCnt + ExtCnt; + if(NameMap.size() != totalDevCnt) + { + // NameMap Size mismatch - Map broken + if (Verbose) fprintf(fp, "M: NameMap size mismatch: %lu!=%lu\n", + static_cast(NameMap.size()), + totalDevCnt); + retVal.retM++; + } + + + //========================================================================== + // Link checks + + // Link integrity checks - Re-linking should fix. + if(AddrMap.size() != (totalDevCnt + SupCnt)) + { + // AddressMap Size mismatch - Link broken + if (Verbose) fprintf(fp, "L: AddrMap Size mismatch: %lu!=%lu\n", + static_cast(AddrMap.size()), + (DevCnt + SupCnt)); + retVal.retL++; + } + + +#ifdef AB_THREADING + // Wait for all of the threads to finish + for(i = 0; i < threadCnt; i++) + { + threads[i].join(); + } +#else + // If we are not using threading, run all of the (long) integrity checks single-threaded. + + //========================================================================== + // Check integrity of DeviceType map + IntegDevTypeMap(Verbose, fp, 0, DevTypes.size(), retVal); + + //========================================================================== + // Device checks + IntegDevices(Verbose, fp, 0, Devices.size(), retVal, ExtConCnt); // Check ALL devices + + //========================================================================== + // External checks + IntegExternals(Verbose, fp, 0, Externals.size(), retVal); // Check ALL externals + + //========================================================================== + // Supervisor checks + IntegSupervisors(Verbose, fp, 0, Supervisors.size(), retVal, MappedSupervisors); // Check ALL supervisors +#endif + + + //========================================================================== + // Check ExtCon size. + if(ExtCon.size() != ExtConCnt) + { + // External Connection vector size mismatch - Map broken + if (Verbose) fprintf(fp, "M: External Connection Vector size mismatch: %lu!=%lu\n", + static_cast(ExtCon.size()), + static_cast(ExtConCnt)); + retVal.retM++; + } + + //========================================================================== + // Check Supervisor map size + if (MappedSupervisors != SupMap.size()) { + // Supervisor Map size missmatch + if (Verbose) fprintf(fp, "L: Supervisor Size mismatch: %u!=%lu\n", + static_cast(MappedSupervisors), + static_cast(SupMap.size())); + retVal.retL++; + } + + //========================================================================== + // Set dirty flags if appropriate + if (Verbose) fprintf(fp, "\nResults:\n"); + + if (retVal.ret > 0) + { + if (Verbose) fprintf(fp, "\tGeneral Breakage: %d\n", static_cast(retVal.ret)); + } + + + if (retVal.retT > 0) // Task integrity compromised - need to rbuild the task + { + TaskValid = false; + if (Verbose) fprintf(fp, "\tDirty Task: %d\n", static_cast(retVal.retT)); + retVal.ret += retVal.retT; + } + + if (retVal.retM > 0) // Map integrity compromised - need to rebuild the map + { + MapValid = false; + if (Verbose) fprintf(fp, "\tDirty Map: %d\n", static_cast(retVal.retM)); + retVal.ret += retVal.retM; + } + + if (retVal.retL > 0) // Link integrity compromised - need to rebuild the link + { + LinkValid = false; + if (Verbose) fprintf(fp, "\tDirty Link: %d\n", static_cast(retVal.retL)); + retVal.ret += retVal.retL; + } + + if (Verbose && (retVal.ret == 0)) fprintf(fp, "\tTask Clean\n"); + + return retVal.ret; +} + +void TaskRecord_t::IntegDevTypeMap(bool Verbose, FILE * fp, unsigned long DTStart, + unsigned long DTEnd, IntegVals_t &retVal) +{ + for(std::vector::iterator D = DevTypes.begin() + DTStart; + (D != DevTypes.end()) && (D < (DevTypes.begin() + DTEnd)); + D++) // Iterate through device type vector + { + unsigned Idx = static_cast(std::distance(DevTypes.begin(), D)); // Current Index + + IdxMap_t::const_iterator DTSearch = DevTypeMap.find(D->first.Name); // Find the DevType name in the map + if (DTSearch == DevTypeMap.end()) { + // DevType not mapped + if (Verbose) fprintf(fp, "T: DevType not mapped: %s\n", + D->first.Name.c_str()); + retVal.retT++; + + } else { + if (DevTypes[DTSearch->second].first.Name != D->first.Name){ + // DevType > Index map is broken. + if (Verbose) fprintf(fp, "T: DevTypeMap Broken: %s!=%s\n", + D->first.Name.c_str(), + DevTypes[DTSearch->second].first.Name.c_str()); + retVal.retT++; + } + + if (Idx != DTSearch->second) { + // Index mismatch + if (Verbose) fprintf(fp, "T: DevTypeMap Index mismatch: %s, %u!=%u\n", + D->first.Name.c_str(), Idx, DTSearch->second); + retVal.retT++; + } + } + + //Check the Input Msg Indexes + for(std::vector::const_iterator I + =D->first.InMsgs.begin();I!=D->first.InMsgs.end();I++) + { + const std::vector *InMsgs = &MsgTypes[*I].Inputs; + if (std::find(InMsgs->begin(), InMsgs->end(), Idx) == InMsgs->end()) + { + // MsgType->DevType (Input Message) Vector broken. + if (Verbose) fprintf(fp, "T: MsgType->DevType(InMsg) Vector broken: \ + DevType %u (%s) not found in MsgType InMsg Vector %u\n", + Idx, D->first.Name.c_str(), *I); //DevTypes[Idx].first.Name + retVal.retT++; + } + } + + //Check the Output Msg Indexes + for(std::vector::const_iterator O + =D->first.OuMsgs.begin();O!=D->first.OuMsgs.end();O++) + { + const std::vector *OuMsgs = &MsgTypes[*O].Outputs; + if (std::find(OuMsgs->begin(), OuMsgs->end(), Idx) == OuMsgs->end()) + { + // MsgType->DevType (Output Message) Vector broken. + if (Verbose) fprintf(fp, "T: MsgType->DevType(OuMsg) Vector broken: \ + DevType %u (%s) not found in MsgType OuMsg Vector %u\n", + Idx, D->first.Name.c_str(), *O); + retVal.retT++; + } + } + } +} + + +void TaskRecord_t::IntegDevices(bool Verbose, FILE * fp, unsigned long DStart, + unsigned long DEnd, IntegVals_t &retVal, + ABULONG &ExtConCnt) +{ + for(std::vector::const_iterator D = Devices.begin() + DStart; + (D != Devices.end()) && (D < (Devices.begin() + DEnd)); + D++) // Iterate through section of Device vector + { + // Check record type + if((D->RecordType != Device) && (D->RecordType != DeviceExt)) + { + // Non-Device in the Device vector. Something, somewhere has gone very VERY wrong + if (Verbose) fprintf(fp, "Non-Device in Device Vector: %s (%" SYMA_FMT "), %u\n", + D->Name.c_str(), D->Address, D->RecordType); + retVal.ret++; + } else { + // Search Addr Map + AddrMap_t::const_iterator DASearch = AddrMap.find(D->Address); // Find the dev Addr in the map + if (DASearch == AddrMap.end()) { + // DevAddr not mapped - Link broken + if (Verbose) fprintf(fp, "L: DevAddr not mapped: %s, %" SYMA_FMT "\n", + D->Name.c_str(), D->Address); + retVal.retL++; + } else if (DASearch->second != &(*D)) { + // Address does not map to THIS device - Link broken + if (Verbose) fprintf(fp, "L: AddrMap to wrong device: %s, %" PTR_FMT "!=%" PTR_FMT "\n", + D->Name.c_str(), reinterpret_cast(DASearch->second), + reinterpret_cast(&(*D))); + retVal.retL++; + } + + // Search Name Map + NameMap_t::const_iterator DNSearch = NameMap.find(D->Name); // Find the dev Name in the map + if (DNSearch == NameMap.end()) { + // DevName not mapped + if (Verbose) fprintf(fp, "M: Device Name not mapped: %s\n", + D->Name.c_str()); + retVal.retM++; + } else if (DNSearch->second != &(*D)) { + // Name does not map to THIS device - Map Broken + if (Verbose) fprintf(fp, "M: Namemap to wrong device: %s, %" PTR_FMT "!=%" PTR_FMT "\n", + D->Name.c_str(), reinterpret_cast(DNSearch->second), + reinterpret_cast(&(*D))); + retVal.retM++; + } + + // Check Device type Index + if (D->DeviceType >= DevTypes.size()) { + // DeviceType Index out of range + if (Verbose) fprintf(fp, "DeviceType Index out of range: %s (%" SYMA_FMT "), DType: %d>%d\n", + D->Name.c_str(), D->Address, D->DeviceType, + static_cast(DevTypes.size())); + retVal.ret++; + } else { + // Find the pointer to the device record in the Device Type vector + const RecordVect_t *DTypeV = &DevTypes[D->DeviceType].second; + if (std::find(DTypeV->begin(), DTypeV->end(), &(*D)) == DTypeV->end()) + { + // Device not in Device Type vector - Map Broken + if (Verbose) fprintf(fp, "M: Device %s (%" SYMA_FMT ") not in DevicetypeVector %u (%s)\n", + D->Name.c_str(), D->Address, D->DeviceType, + DevTypes[D->DeviceType].first.Name.c_str()); + retVal.retM++; + } + } + + // Search Supervisor + SupMap_t::const_iterator DSSearch = SupMap.find(D->Supervisor); // Find the dev Addr in the map + if (DSSearch == SupMap.end()) { + // Device's Supervisor not found - Link broken + if (Verbose) fprintf(fp, "L: Dev's Supervisor not found in AddrMap: %s, %" SYMA_FMT "\n", + D->Name.c_str(), D->Supervisor); + retVal.retL++; + } else { + // Search Supervsor Vector + const RecordVect_t *SupV = &DSSearch->second; + if (std::find(SupV->begin(), SupV->end(), &(*D)) == SupV->end()) + { + // Pointer to Device Record not found in Supervisor Vector. + if (Verbose) fprintf(fp, "L: Ptr to Device not in Supervisor \ + vector: %s, Supervisor: %" SYMA_FMT "\n", + D->Name.c_str(), D->Supervisor); + retVal.retL++; + } + } + + // Check if the Attribute Index is out of range + if(D->Attribute >= static_cast(AttrTypes.size())) + { + // Attribute Index out of range. + if (Verbose) fprintf(fp, "Attribute Index out of range: %s (%" SYMA_FMT "), Attr: %d>%d\n", + D->Name.c_str(), D->Address, D->Attribute, + static_cast(AttrTypes.size())); + retVal.ret++; + } else if (D->Attribute > -1) { + // Find the pointer to the device record in the Attribute Type vector + const RecordVect_t *ATypeV = &AttrTypes[static_cast(D->Attribute)].second; + if (std::find(ATypeV->begin(), ATypeV->end(), &(*D)) == ATypeV->end()) + { + // Device not in Attribute Type vector - Map Broken + if (Verbose) fprintf(fp, "L: Dev not in Attribute vector: %s\ + , Attribute: %d (%s)\n", D->Name.c_str(), D->Attribute, + AttrTypes[static_cast(D->Attribute)].first.c_str()); + retVal.retM++; + } + } + + // If ExtCon, search ExtCon Vector, ExtConCnt++ + if(D->RecordType == DeviceExt) + { + if (std::find(ExtCon.begin(), ExtCon.end(), &(*D)) == ExtCon.end()) + { + // Device with External Connection not found in ExtCon - Map Broken + if (Verbose) fprintf(fp, "M: Device with External connection not in\ + ExtCon: %s (%" SYMA_FMT ")\n", D->Name.c_str(), D->Address); + retVal.retM++; + } + ExtConCnt++; + } + } + } +} + +void TaskRecord_t::IntegExternals(bool Verbose, FILE * fp, unsigned long EStart, + unsigned long EEnd, IntegVals_t &retVal) +{ + for(std::vector::const_iterator E = Externals.begin() + EStart; + (E != Externals.end()) && (E < (Externals.begin() + EEnd)); + E++) // Iterate through External vector + { + // Check record type + if(E->RecordType != External) + { + // Non-External in the External vector. Something, somewhere has gone very VERY wrong + if (Verbose) fprintf(fp, "Non-External in External Vector: %s, \ + %" SYMA_FMT ", %d\n", E->Name.c_str(), E->Address, E->RecordType); + retVal.ret++; + } else { + // Search Addr Map + AddrMap_t::const_iterator EASearch = AddrMap.find(E->Address); // Find the dev Addr in the map + if (EASearch == AddrMap.end()) { + // ExtAddr not mapped - Link broken + if (Verbose) fprintf(fp, "L: ExtAddr not mapped: %s, %" SYMA_FMT "\n", + E->Name.c_str(), E->Address); + retVal.retL++; + } else if (EASearch->second != &(*E)) { + // Address does not map to THIS external - Link broken + if (Verbose) fprintf(fp, "L: AddrMap to wrong External: %s, %" PTR_FMT "!=%" PTR_FMT "\n", + E->Name.c_str(), reinterpret_cast(EASearch->second), + reinterpret_cast(&(*E))); + retVal.retL++; + } + + // Search Name Map + NameMap_t::const_iterator ENSearch = NameMap.find(E->Name); // Find the dev Name in the map + if (ENSearch == NameMap.end()) { + // ExtName not mapped + if (Verbose) fprintf(fp, "M: External Name not mapped: %s (%" SYMA_FMT ")\n", + E->Name.c_str(), E->Address); + retVal.retM++; + } else if (ENSearch->second != &(*E)) { + // Name does not map to THIS external - Map Broken + if (Verbose) fprintf(fp, "M: Namemap to wrong external: %s, %" PTR_FMT "!=%" PTR_FMT "\n", + E->Name.c_str(), reinterpret_cast(ENSearch->second), + reinterpret_cast(&(*E))); + retVal.retM++; + } + + // Check Device type Index + if (E->DeviceType >= DevTypes.size()) { + // DeviceType Index out of range + if (Verbose) fprintf(fp, "DeviceType Index out of range: %s (%" SYMA_FMT ")\ + , DType: %u>%d\n", E->Name.c_str(), E->Address, E->DeviceType, + static_cast(DevTypes.size())); + retVal.ret++; + } else { + // Find the pointer to the device record in the Device Type vector + const RecordVect_t *DTypeV = &DevTypes[E->DeviceType].second; + if (std::find(DTypeV->begin(), DTypeV->end(), &(*E)) == DTypeV->end()) + { + // External not in Device Type vector - Map Broken + if (Verbose) fprintf(fp, "M: External %s (%" SYMA_FMT ") not in \ + DeviceType Vector %u (%s)\n", + E->Name.c_str(), E->Address, E->DeviceType, + DevTypes[E->DeviceType].first.Name.c_str()); + retVal.retM++; + } + } + + // Check if the Attribute Index is out of range + if(E->Attribute >= static_cast(AttrTypes.size())) + { + // Attribute Index out of range. + if (Verbose) fprintf(fp, "Attribute Index out of range: %s (%" SYMA_FMT "), \ + Attr: %d>%d\n", E->Name.c_str(), E->Address, E->Attribute, + static_cast(AttrTypes.size())); + retVal.ret++; + } else if (E->Attribute > -1) { + // Find the pointer to the external record in the Attribute Type vector + const RecordVect_t *ATypeV = &AttrTypes[static_cast(E->Attribute)].second; + if (std::find(ATypeV->begin(), ATypeV->end(), &(*E)) == ATypeV->end()) + { + // External not in Attribute Type vector - Map Broken + if (Verbose) fprintf(fp, "L: Ext not in Attribute vector: %s, \ + Attribute: %d (%s)\n", E->Name.c_str(), E->Attribute, + AttrTypes[static_cast(E->Attribute)].first.c_str()); + retVal.retM++; + } + } + } + } +} + +void TaskRecord_t::IntegSupervisors(bool Verbose, FILE * fp, unsigned long SStart, + unsigned long SEnd, IntegVals_t &retVal, + ABUNSIGNED &MappedSupervisors) +{ + for(std::vector::const_iterator S = Supervisors.begin() + SStart; + (S != Supervisors.end()) && (S < (Supervisors.begin() + SEnd)); + S++) // Iterate through Supervisor vector + { + // Check record type + if(S->RecordType != Supervisor) + { + // Non-Supervisor in the Supervisor vector. Something, somewhere has gone very VERY wrong + if (Verbose) fprintf(fp, "Non-Supervisor in Supervisor Vector: %s, \ + %" SYMA_FMT ", %d\n", S->Name.c_str(), S->Address, S->RecordType); + retVal.ret++; + } else { + // Check that Supervisor is listed in Addr Map + AddrMap_t::const_iterator SASearch = AddrMap.find(S->Address); // Find the Addr in the map + if (SASearch == AddrMap.end()) { + // SupAddr not mapped - Link broken + if (Verbose) fprintf(fp, "L: SupAddr not mapped: %s, %" SYMA_FMT "\n", + S->Name.c_str(), S->Address); + retVal.retL++; + } else if (SASearch->second != &(*S)) { + // Address does not map to THIS supervisor - Link broken + if (Verbose) fprintf(fp, "L: AddrMap to wrong Supervisor: %s, %" PTR_FMT "!=%" PTR_FMT "\n", + S->Name.c_str(), reinterpret_cast(SASearch->second), + reinterpret_cast(&(*S))); + retVal.retL++; + } + + // Check that Supervisor is listed in SupMap + SupMap_t::const_iterator SMSearch = SupMap.find(S->Address); + if (SMSearch == SupMap.end()) + { + // SupAddr not mapped - Link broken + if (Verbose) fprintf(fp, "L: SupAddr not in SupMap: %s, %" SYMA_FMT "\n", + S->Name.c_str(), S->Address); + retVal.retL++; + } else { + MappedSupervisors++; + + // Check all devices allocated to supervisor use THIS supervisor + for(RecordVect_t::const_iterator SMV + =SMSearch->second.begin();SMV!=SMSearch->second.end();SMV++) // Iterate through Supervisor's devices + { + if((*SMV)->Supervisor != SMSearch->first) + { + // Supervisor Address in device record does not match THIS supervisor - Link broken + if (Verbose) fprintf(fp, "L: Dev SupAddr mismatch: %s, %"\ + SYMA_FMT "!=%" SYMA_FMT "\n", (*SMV)->Name.c_str(), + (*SMV)->Supervisor, SMSearch->first); + retVal.retL++; + } + } + } + + // Check Device type Index + if (S->DeviceType >= DevTypes.size()) { + // DeviceType Index out of range + if (Verbose) fprintf(fp, "DeviceType Index out of range: Supervisor (%" SYMA_FMT \ + "), DType: %u>%d\n", S->Address, S->DeviceType, + static_cast(DevTypes.size())); + retVal.ret++; + } else { + // Find the pointer to the device record in the Device Type vector + const RecordVect_t *DTypeV = &DevTypes[S->DeviceType].second; + if (std::find(DTypeV->begin(), DTypeV->end(), &(*S)) == DTypeV->end()) + { + // Supervisor not in Device Type vector - Map Broken + if (Verbose) fprintf(fp, "M: Supervisor (%" SYMA_FMT \ + ") not in DeviceType Vector %u (%s)\n", + S->Address, S->DeviceType, + DevTypes[S->DeviceType].first.Name.c_str()); + retVal.retM++; + } + } + } + } +} + +} /* namespace AddressBookNS */ diff --git a/Source/NameServer/ABTask.hpp b/Source/NameServer/ABTask.hpp new file mode 100644 index 00000000..bff1c296 --- /dev/null +++ b/Source/NameServer/ABTask.hpp @@ -0,0 +1,139 @@ +#ifndef ABTask_H +#define ABTask_H + +#include "ABDefs.hpp" +#include "ABRecord.hpp" + +#include +#include +#include + +#if (__cplusplus >= 201103) && (\ + (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) ||\ + defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))) + //C++11+ and C11+ or POSIX: + // fprintf is thread safe so let's thread stuff! + #include + #include + #define AB_THREADING + + // We need Atomic types if we are using threading. + #define ABUNSIGNED std::atomic + #define ABULONG std::atomic +#else + #define ABUNSIGNED unsigned + #define ABULONG unsigned long + +#endif + +namespace AddressBookNS +{ + +typedef std::map IdxMap_t; +typedef std::vector RecordVect_t; +typedef std::map SupMap_t; +typedef std::map RecordMap_t; +typedef std::map AddrMap_t; +typedef std::map NameMap_t; + +typedef std::pair DevTypePair; +typedef std::pair AttrTypePair; + +struct IntegVals_t { + ABUNSIGNED ret; + ABUNSIGNED retT; + ABUNSIGNED retL; + ABUNSIGNED retM; +}; + +struct TaskData_t { + TaskData_t(); + TaskData_t(std::string &N, std::string &P, std::string &X, std::string &E, + TaskState_t S, unsigned long DC, unsigned long EC); + + int size() const; // Get the size (in bytes) of the TaskData + + std::string Name; // = ""; + std::string Path; // = ""; + std::string XML; // = ""; + std::string ExecutablePath; // = ""; + TaskState_t State; // = Loaded; + unsigned long DeviceCount; // = 0; // The maximum number of devices in task + unsigned long DeviceCountLd; // = 0; // The number of devices currently loaded + unsigned long ExternalCount; // = 0; // The maximum number of externals in task + unsigned long ExternalCountLd; // = 0; // The number of externals currently loaded + unsigned long SupervisorCount; // = 0; // The number of allocated supervisors + std::vector DeviceTypes; + std::vector MessageTypes; + std::vector AttributeTypes; +}; + + +class TaskRecord_t { +public: + TaskRecord_t(); + TaskRecord_t(std::string &N, std::string &P, std::string &X, std::string &E, + TaskState_t S, unsigned long DC, unsigned long EC); + TaskRecord_t(TaskData_t &Data); + + unsigned Integrity(bool Verbose = false, FILE * = stdout); + + int size() const; // Get the size (in bytes) of the TaskData + + + std::string Name; // = ""; + std::string Path; // = ""; + std::string XML; // = ""; + std::string ExecPath; // = ""; + unsigned long DevCntMax; // = 0; // The expected number of devices + unsigned long ExtCntMax; // = 0; // The expected number of external devices + unsigned long DevCnt; // = 0; // The loaded number of Devices + unsigned long SupCnt; // = 0; // The loaded number of Supervisors + unsigned long ExtCnt; // = 0; // The loaded number of External Devices + + TaskState_t State; // = Loaded; //State of the task + + std::vector Devices; // List of Devices in the task + std::vector Externals; // List of Externals in the task + std::vector Supervisors; // List of Supervisors in the task + + std::vector MsgTypes; // List of task Msg types from XML + //std::vector MsgTypes; // List of task Msg types from XML + std::vector DevTypes; // List of task Dev Types from XML + std::vector AttrTypes; // List of task Attrs from XML + + RecordVect_t ExtCon; // List of Devices with external conns + + // Task String Data Maps + IdxMap_t MsgTypeMap; // Map Msg type string to Idx + IdxMap_t DevTypeMap; // Map Dev type Name to Idx + IdxMap_t AttrTypeMap; // Map Attr type string to Idx + + //Device Maps + SupMap_t SupMap; // Supervisor>DeviceAddress map + AddrMap_t AddrMap; // Addr>record map basis of DNAME, DGRP, SNAME + NameMap_t NameMap; // Name>record map basis of DNAME, DGRP, SNAME + + bool TaskValid; // = true; // Indicate whether the task string info is valid + bool MapValid; // = true; // Indicate whether the task maps are valid + bool LinkValid; // = true; // Indicate whether the Device > Address links are valid + +private: + void IntegDevices(bool Verbose, FILE * fp, unsigned long DStart, + unsigned long DEnd, IntegVals_t &retVal, + ABULONG &ExtConCnt); + + void IntegExternals(bool Verbose, FILE * fp, unsigned long EStart, + unsigned long EEnd, IntegVals_t &retVal); + + void IntegSupervisors(bool Verbose, FILE * fp, unsigned long SStart, + unsigned long SEnd, IntegVals_t &retVal, + ABUNSIGNED &MappedSupervisors); + + void IntegDevTypeMap(bool Verbose, FILE * fp, unsigned long DTStart, + unsigned long DTEnd, IntegVals_t &retVal); +}; + + +} /* namespace AddressBookNS */ +#endif /* ABTask_H */ diff --git a/Source/NameServer/AddressBook.cpp b/Source/NameServer/AddressBook.cpp new file mode 100644 index 00000000..a78e8af6 --- /dev/null +++ b/Source/NameServer/AddressBook.cpp @@ -0,0 +1,1352 @@ +//============================================================================== +#include "AddressBook.hpp" + +#include + +//============================================================================== +namespace AddressBookNS +{ +#ifdef EXCEPTS +#define ERETURN(X, Y) throw(X) +#else +#define ERETURN(X, Y) return Y +#endif + +// static constant definitions +const unsigned AddressBook::SUCCESS; +const unsigned AddressBook::ERR_INVALID_TASK; +const unsigned AddressBook::ERR_INVALID_DEVTYPE; +const unsigned AddressBook::ERR_INVALID_DEVICE; +const unsigned AddressBook::ERR_INVALID_MESSAGE_TYPE; +const unsigned AddressBook::ERR_INVALID_ATTRIBUTE; +const unsigned AddressBook::ERR_DEVICE_DATA_MISMATCH; +const unsigned AddressBook::ERR_TASKNAME_USED; +const unsigned AddressBook::ERR_DEVICENAME_USED; +const unsigned AddressBook::ERR_DEVICE_ADDR_USED; +const unsigned AddressBook::ERR_TASK_NOT_FOUND; +const unsigned AddressBook::ERR_DEVICE_NOT_FOUND; +const unsigned AddressBook::ERR_INVALID_MAP; +const unsigned AddressBook::ERR_INVALID_SUPERVISOR; + +//Constructors +AddressBook::AddressBook(std::string d) +{ + ABderived = d; + TaskCount = 0; +} + +AddressBook::~AddressBook() +{ + // Teardown the TaskMap. Everything else should be handled properly. + for(TaskMap_t::iterator T=TaskMap.begin();T!=TaskMap.end();T++) + { + TaskRecord_t* TRec = &(*(T->second)); + delete TRec; + + } +} + + +/*============================================================================== + * GetTaskCount: Get the number of currently loaded tasks + *============================================================================*/ +unsigned AddressBook::GetTaskCount(void) +{ + return TaskMap.size(); +} + +//============================================================================== +//Task Manipulation +unsigned AddressBook::AddTask(const std::string &TaskName, TaskData_t &Data) +{ + // Check that the task does not already exist. + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec != PNULL) + { + //throw("Task Name Already Used"); + return ERR_TASKNAME_USED; + } + + // validate here and exit if the device type data is invalid. These will + // actually be checked again when device types are added below; a duplication, + // but the number of device types and number of message types is going to + // be relatively small, so this isn't going to be too costly + unsigned Ret = ValidateTask(Data); + if(Ret) return Ret; + + // Build a TaskRecord + TaskRecord_t * Task = new TaskRecord_t; + + Task->Name = TaskName; + Task->Path = Data.Path; + Task->XML = Data.XML; + Task->ExecPath = Data.ExecutablePath; + + Task->DevCntMax = Data.DeviceCount; + Task->ExtCntMax = Data.ExternalCount; + + // Pre-reserve the Vectors of Records for consistent pointers. + Task->Devices.reserve(Data.DeviceCount + 1); + Task->Externals.reserve(Data.ExternalCount + 1); + Task->Supervisors.reserve(MAXSUPERVISORS + 1); + + // String Data - copy it over, initialise vectors and create index maps. + // MessageType strings. + Task->MsgTypes.resize(Data.MessageTypes.size()); //pre-alloc vector + std::vector *MTypes = &(Data.MessageTypes); + for(std::vector::const_iterator M=MTypes->begin(); + M!=MTypes->end(); M++) + { + Task->MsgTypes[static_cast(M - MTypes->begin())].Name = *M; + Task->MsgTypeMap.insert(IdxMap_t::value_type(*M, (M-MTypes->begin()))); + } + + // AttributeType strings. + Task->AttrTypes.resize(Data.AttributeTypes.size()); + std::vector *ATypes = &(Data.AttributeTypes); + for(std::vector::const_iterator A=ATypes->begin(); + A!=ATypes->end(); A++) + { + AttrTypePair APair(*A,RecordVect_t()); + Task->AttrTypes[static_cast(A - ATypes->begin())] = APair; + Task->AttrTypeMap.insert(IdxMap_t::value_type(*A, (A-ATypes->begin()))); + } + + // Add to the Task Map. + TaskMap.insert(TaskMap_t::value_type(Task->Name, Task)); + ++TaskCount; // Pedantic: avoid assignment to temporary 5 July 2019 ADR + + /* Insert DeviceTypes. Changed order and call subfunction 5 July 2019 ADR + Original had pre-allocation below, but it's hard to see how this + really gains anything because a DevTypeRecord_t contains 2 internal + vectors (which would be zero-initialised) and a resize is thus inevitable + anyway when actual device types are inserted. + */ + // Task->DevTypes.resize(Data.DeviceTypes.size()); //pre-alloc vector + std::vector *DTypes = &(Data.DeviceTypes); + for(std::vector::iterator D=DTypes->begin(); + D!=DTypes->end(); D++) + AddDeviceType(Task->Name, *D); // add is bound to succeed because we have + // pre-validated. + return SUCCESS; +} + +/*============================================================================== + * AddDeviceType: Append another device type to the task. + *============================================================================*/ +unsigned AddressBook::AddDeviceType(const std::string &TaskName, const DevTypeRecord_t &DeviceType) +{ + // Check that the task exists. + TaskRecord_t *Task = FindTask(TaskName); + if(Task == PNULL) + { + return ERR_TASK_NOT_FOUND; + } + // and that the new device type has valid message indices + if (unsigned retVal = ValidateDeviceType(DeviceType, Task->MsgTypes.size())) + return retVal; + DevTypePair DPair(DeviceType,RecordVect_t()); + Task->DevTypes.push_back(DPair); + unsigned devTypIdx = Task->DevTypes.size() - 1; + Task->DevTypeMap.insert(IdxMap_t::value_type(DeviceType.Name, devTypIdx)); + + // Cross-ref to Message Types. + for(std::vector::const_iterator I=DeviceType.InMsgs.begin(); + I!=DeviceType.InMsgs.end(); I++) // Input Messages + { + Task->MsgTypes[*I].Inputs.push_back(devTypIdx); + } + for(std::vector::const_iterator O=DeviceType.OuMsgs.begin(); + O!=DeviceType.OuMsgs.end(); O++) // Output Messages + { + Task->MsgTypes[*O].Outputs.push_back(devTypIdx); + } + return SUCCESS; +} + +/*============================================================================== + * ClearTask: Removes all the devices from a task + *============================================================================*/ +unsigned AddressBook::ClearTask(const std::string &TaskName) +{ + // The easiest way to do this is simply to copy the data to be saved into + // a TaskData_t object, remove the task, then re-add it with all the device + // data removed. + TaskData_t cTask; + unsigned err = SUCCESS; + if ((err = GetTask(TaskName, cTask)) != SUCCESS) return err; + cTask.DeviceCount = 0; + cTask.DeviceCountLd = 0; + cTask.ExternalCount = 0; + cTask.ExternalCountLd = 0; + cTask.SupervisorCount = 0; + cTask.DeviceTypes.clear(); + if ((err = DelTask(TaskName)) != SUCCESS) return err; + return AddTask(TaskName, cTask); +} + +/*============================================================================== + * ListTask: Populate a vector with all of the names of loaded tasks + *============================================================================*/ +unsigned AddressBook::ListTask(std::vector &Tasks) +{ + // Enumerate the Task List. + for(TaskMap_t::iterator T=TaskMap.begin();T!=TaskMap.end();T++) + { + Tasks.push_back(T->second->Name); + } + return SUCCESS; +} + +/*============================================================================== + * GetTask: Get a task by name + *============================================================================*/ +unsigned AddressBook::GetTask(const std::string &TaskName, TaskData_t &Task) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + //throw("Task Does Not Exist"); + return ERR_TASK_NOT_FOUND; + } + + Task.Name = TRec->Name; + Task.Path = TRec->Path; + Task.XML = TRec->XML; + Task.ExecutablePath = TRec->ExecPath; + Task.State = TRec->State; + Task.DeviceCount = TRec->DevCntMax; + Task.DeviceCountLd = TRec->DevCnt; + Task.ExternalCount = TRec->ExtCntMax; + Task.ExternalCountLd = TRec->ExtCnt; + Task.SupervisorCount = TRec->SupCnt; + + // Copy MessageType strings + for(std::vector::const_iterator M=TRec->MsgTypes.begin(); + M!=TRec->MsgTypes.end(); M++) + { + Task.MessageTypes.push_back(M->Name); + } + + // Copy DeviceType strings + for(std::vector::const_iterator D=TRec->DevTypes.begin(); + D!=TRec->DevTypes.end(); D++) + { + Task.DeviceTypes.push_back(D->first); + } + + // Copy AttributeType strings + for(std::vector::const_iterator A=TRec->AttrTypes.begin(); + A!=TRec->AttrTypes.end(); A++) + { + Task.AttributeTypes.push_back(A->first); + } + + return SUCCESS; +} + +/*============================================================================== + * DelTask: Delete the named task + *============================================================================*/ +unsigned AddressBook::DelTask(const std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + //throw("Task Does Not Exist"); + return ERR_TASK_NOT_FOUND; + } + + // Erase it + delete TRec; + TaskMap.erase(TaskName); + + TaskCount--; + + return SUCCESS; +} + +/*============================================================================== + * TaskState: Get the state of the task + *============================================================================*/ +TaskState_t AddressBook::TaskState(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + //throw("Task Does Not Exist"); + return static_cast(Unknown); + } + + return TRec->State; +} + +/*============================================================================== + * TaskState: Set the state of the task + *============================================================================*/ +unsigned AddressBook::TaskState(std::string &TaskName, TaskState_t State) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + //throw("Task Does Not Exist"); + return ERR_TASK_NOT_FOUND; + } + + TRec->State = State; + + return SUCCESS; +} + +/*============================================================================== + * TaskExecPath: Get the path to the executables for the task + *============================================================================*/ +std::string AddressBook::TaskExecPath(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + //throw("Task Does Not Exist"); + return ""; + } + + return TRec->ExecPath; +} + +/*============================================================================== + * TaskExecPath: Set the path to the executables for the task + *============================================================================*/ +unsigned AddressBook::TaskExecPath(std::string &TaskName, std::string &NewPath) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + TRec->ExecPath = NewPath; + return SUCCESS; +} + +/*============================================================================== + * TaskValid: Indicates whether the Task's data is valid + *============================================================================*/ +bool AddressBook::TaskValid(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", false); + } + + return TRec->TaskValid; +} + +/*============================================================================== + * TaskMapValid: Indicates whether the Task's mappings are valid + *============================================================================*/ +bool AddressBook::TaskMapValid(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", false); + } + + return TRec->MapValid; +} + +/*============================================================================== + * TaskLinkValid: Indicates whether the Task's linkage is valid + *============================================================================*/ +bool AddressBook::TaskLinkValid(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", false); + } + + return TRec->LinkValid; +} + + +/*============================================================================== + * RebuildTask: Rebuild the Task's MessageType/DeviceType maps + *============================================================================*/ +unsigned AddressBook::RebuildTask(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + //TODO: test this + + // Rebuild MessageType map + TRec->MsgTypeMap.clear(); + for(std::vector::iterator M=TRec->MsgTypes.begin(); + M!=TRec->MsgTypes.end(); M++) + { + M->Inputs.clear(); // Clear the Input vector data. + M->Outputs.clear(); // Clear the Output vector data. + TRec->MsgTypeMap.insert(IdxMap_t::value_type(M->Name, (M-TRec->MsgTypes.begin()))); + } + + //Rebuild DeviceType Map and links to MessageType - leave the contents of the Device Vector alone! + TRec->DevTypeMap.clear(); + for(std::vector::iterator D=TRec->DevTypes.begin(); + D!=TRec->DevTypes.end(); D++) + { + TRec->DevTypeMap.insert(IdxMap_t::value_type(D->first.Name, (D-TRec->DevTypes.begin()))); + + // Cross-ref to Message Types. + for(std::vector::iterator I=D->first.InMsgs.begin(); + I!=D->first.InMsgs.end(); I++) // Input Messages + { + TRec->MsgTypes[*I].Inputs.push_back(static_cast(D-TRec->DevTypes.begin())); + } + for(std::vector::iterator O=D->first.OuMsgs.begin(); + O!=D->first.OuMsgs.end(); O++) // Output Messages + { + TRec->MsgTypes[*O].Outputs.push_back(static_cast(D-TRec->DevTypes.begin())); + } + } + + //Rebuild AttributeType map - leave the contents of the Device Vector alone! + TRec->AttrTypeMap.clear(); + for(std::vector::iterator A=TRec->AttrTypes.begin(); + A!=TRec->AttrTypes.end(); A++) + { + TRec->AttrTypeMap.insert(IdxMap_t::value_type(A->first, (A-TRec->AttrTypes.begin()))); + } + + return SUCCESS; +} + +/*============================================================================== + * BuildMaps: Build Task Maps + *============================================================================*/ +unsigned AddressBook::BuildMaps(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(TRec->MapValid || TRec->NameMap.size() > 0 + || TRec->ExtCon.size() > 0) ClearMap(TRec); // Existing map, clear it + + + // Map all of the Devices + for(std::vector::iterator DRec=TRec->Devices.begin(); + DRec!=TRec->Devices.end(); DRec++) + { + MapDevice(TRec, &(*DRec)); + } + + // Map all of the Externals + for(std::vector::iterator ERec=TRec->Externals.begin(); + ERec!=TRec->Externals.end(); ERec++) + { + MapDevice(TRec, &(*ERec)); + } + + // Map all of the Supervisors + for(std::vector::iterator SRec=TRec->Supervisors.begin(); + SRec!=TRec->Supervisors.end(); SRec++) + { + // Add the supervisor to the correct DeviceType list. + TRec->DevTypes[SRec->DeviceType].second.push_back(&(*SRec)); + } + + TRec->MapValid = true; // added 18 July 2019 ADR update the map status + + return SUCCESS; +} + +/*============================================================================== + * BuildLink: Build Task linkage + *============================================================================*/ +unsigned AddressBook::BuildLink(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(TRec->LinkValid || TRec->AddrMap.size() > 0) ClearLink(TRec); // Existing link, clear it + + // Link all of the Devices + for(std::vector::iterator DRec=TRec->Devices.begin(); + DRec!=TRec->Devices.end(); DRec++) + { + LinkDevice(TRec, &(*DRec)); + } + + // Link all of the Externals + for(std::vector::iterator ERec=TRec->Externals.begin(); + ERec!=TRec->Externals.end(); ERec++) + { + TRec->AddrMap.insert(AddrMap_t::value_type(ERec->Address, &(*ERec))); + } + + // Link all of the Supervisors + for(std::vector::iterator SRec=TRec->Supervisors.begin(); + SRec!=TRec->Supervisors.end(); SRec++) + { + TRec->AddrMap.insert(AddrMap_t::value_type(SRec->Address, &(*SRec))); + } + + TRec->LinkValid = true; // added 18 July 2019 ADR update the link status + + return SUCCESS; +} + + +//============================================================================== +//Device Addition +unsigned AddressBook::AddDevice(std::string &TaskName, Record_t &DevRec, bool Validate) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + Record_t *DRec; + + if (Validate) // Check that the device does not already exist + { // and that the indices are valid. + unsigned Ret = ValidateDevice(TRec, DevRec); + if(Ret) return Ret; + } + + if ((DevRec.RecordType == Device) || (DevRec.RecordType == DeviceExt)) + { + // Add a Device + CheckVector(TRec, TRec->Devices); // Sanity check for reallocation. + + TRec->Devices.push_back(DevRec); // Add Dev to task. + TRec->DevCnt++; + DRec = &TRec->Devices.back(); + + LinkDevice(TRec, DRec); // Add Device to name map & supervisor list. + MapDevice(TRec, DRec); // Add dev to other maps & lists. + + } else if (DevRec.RecordType == External) { + // Add an External + CheckVector(TRec, TRec->Externals); // Sanity check for reallocation + + TRec->Externals.push_back(DevRec); + TRec->ExtCnt++; + DRec = &TRec->Externals.back(); + + TRec->AddrMap.insert(AddrMap_t::value_type(DRec->Address, DRec)); + MapDevice(TRec, DRec); // Add dev to other maps & lists. + + } else if (DevRec.RecordType == Supervisor) { + // Add a Supervisor + CheckVector(TRec, TRec->Supervisors); // Sanity check for reallocation. + + TRec->Supervisors.push_back(DevRec); + TRec->SupCnt++; + DRec = &TRec->Supervisors.back(); + + TRec->AddrMap.insert(AddrMap_t::value_type(DRec->Address, DRec)); + + // Add the supervisor to the correct DeviceType list. + TRec->DevTypes[DRec->DeviceType].second.push_back(DRec); + + SupMap_t::iterator SSearch = TRec->SupMap.find(DRec->Address); + if (SSearch == TRec->SupMap.end()) + { //Supervisor is NOT in the map yet. + TRec->SupMap.insert(SupMap_t::value_type(DRec->Address, + RecordVect_t())); + } + } + // added 18 July 2019 ADR - if Map and Link were previously invalidated + // rebuild everything + unsigned err; + if (!TRec->MapValid && ((err = BuildMaps(TRec->Name)) != SUCCESS)) return err; + if (!TRec->LinkValid && ((err = BuildLink(TRec->Name)) != SUCCESS)) return err; + return SUCCESS; +} + + +/*============================================================================== + * UpdateDevice: Update a device with a new address and supervisor. + * This implicitly marks the Linkage as invalid. + *============================================================================*/ +unsigned AddressBook::UpdateDevice(std::string &TaskName, DeviceData_t &Data) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + // Check that the Device Name exists + NameMap_t::iterator DSearch = TRec->NameMap.find(Data.Name); + if (DSearch == TRec->NameMap.end()) { + return ERR_DEVICE_NOT_FOUND; + } + Record_t *DRec = DSearch->second; + + TRec->LinkValid = false; // Mark the Task linkage as dirty + DRec->Address = Data.Address; // Update the Device Address + DRec->Supervisor = Data.Supervisor; // Update the Device Supervisor + return SUCCESS; +} + +/*============================================================================== + * GetDeviceCount: Get the number of devices reserved in the named task + *============================================================================*/ +long AddressBook::GetDeviceCount(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_BAD_COUNT); + } + + return static_cast(TRec->DevCntMax); +} + +/*============================================================================== + * GetLoadedDeviceCount: Get the number of devices loaded in the named task + *============================================================================*/ +long AddressBook::GetLoadedDeviceCount(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_BAD_COUNT); + } + + return static_cast(TRec->DevCnt); +} + +/*============================================================================== + * GetExternalCount: Get the number of externals reserved in the named task + *============================================================================*/ +long AddressBook::GetExternalCount(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_BAD_COUNT); + } + + return static_cast(TRec->ExtCntMax); +} + +/*============================================================================== + * GetLoadedExternalCount: Get the number of externals loaded in the named task + *============================================================================*/ +long AddressBook::GetLoadedExternalCount(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_BAD_COUNT); + } + + return static_cast(TRec->ExtCnt); +} + +/*============================================================================== + * GetSupervisorCount: Get the number of supervisors allocated to the task + *============================================================================*/ +int AddressBook::GetSupervisorCount(std::string &TaskName) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_BAD_COUNT); + } + + return static_cast(TRec->SupCnt); +} + + +//============================================================================== +//Device Queries + +/*============================================================================== + * FindDevice: Find a device by Address. + * Gives a Pointer to Const Device Record + *============================================================================*/ +unsigned AddressBook::FindDevice(std::string &TaskName, SymAddr_t Address, const Record_t* &DRec) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + AddrMap_t::iterator DSearch = TRec->AddrMap.find(Address); // Find the Device + if (DSearch == TRec->AddrMap.end()) { + ERETURN("Device Not Found", ERR_DEVICE_NOT_FOUND); + } + + DRec = &(*DSearch->second); + return SUCCESS; +} + + +/*============================================================================== + * FindDevice: Find a device by Name. + * Gives a Pointer to Const Device Record + *============================================================================*/ +unsigned AddressBook::FindDevice(std::string &TaskName, std::string &Name, const Record_t* &DRec) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + NameMap_t::iterator DSearch = TRec->NameMap.find(Name); // Find the Device + if (DSearch == TRec->NameMap.end()) { + ERETURN("Device Not Found", ERR_DEVICE_NOT_FOUND); + } + + DRec = &(*DSearch->second); + return SUCCESS; +} + + +/*============================================================================== + * FindBySuper: Find a vector of devices supervised by Supervisor. + * Gives a Pointer to a const Vector of Pointers to const Device + * Records. + *============================================================================*/ +unsigned AddressBook::FindBySuper(std::string &TaskName, SymAddr_t Supervisor, const RecordVect_t* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + SupMap_t::iterator SSearch = TRec->SupMap.find(Supervisor); // Find the Supervisor + if (SSearch == TRec->SupMap.end()) { + ERETURN("Device Not Found", ERR_DEVICE_NOT_FOUND); + } + + Records = &SSearch->second; + return SUCCESS; +} + +unsigned AddressBook::FindByType(std::string &TaskName, std::string Type, const RecordVect_t* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + IdxMap_t::iterator TSearch = TRec->DevTypeMap.find(Type); // Find the Index + if (TSearch == TRec->DevTypeMap.end()) { + ERETURN("Device Type Not Found", ERR_DEVICE_NOT_FOUND); + } + + Records = &(TRec->DevTypes[TSearch->second].second); + + return SUCCESS; +} + +unsigned AddressBook::FindByAttribute(std::string &TaskName, std::string Attribute, const RecordVect_t* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + IdxMap_t::iterator ASearch = TRec->AttrTypeMap.find(Attribute); // Find the Index + if (ASearch == TRec->AttrTypeMap.end()) { + ERETURN("Device Type Not Found", ERR_DEVICE_NOT_FOUND); + } + + Records = &(TRec->AttrTypes[ASearch->second].second); + + return SUCCESS; +} + +unsigned AddressBook::FindByInMsg(std::string &TaskName, std::string Msg, const RecordVect_t* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + return SUCCESS; +} + +unsigned AddressBook::FindByOuMsg(std::string &TaskName, std::string Msg, const RecordVect_t* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + return SUCCESS; +} + +unsigned AddressBook::GetDevices(std::string &TaskName, const std::vector* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + Records = &(TRec->Devices); + + return SUCCESS; +} + +unsigned AddressBook::GetExternals(std::string &TaskName, const std::vector* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + Records = &(TRec->Externals); + + return SUCCESS; +} + +unsigned AddressBook::GetSupervisors(std::string &TaskName, const std::vector* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + Records = &(TRec->Supervisors); + + return SUCCESS; +} + +unsigned AddressBook::GetExtCon(std::string &TaskName, const RecordVect_t* &Records) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + if(!(TRec->TaskValid && TRec->MapValid && TRec->LinkValid)) // Return NotFound if dirty + { + return ERR_INVALID_MAP; + } + + Records = &(TRec->ExtCon); + + return SUCCESS; +} + + + + +//============================================================================== +//Private Methods + +/*============================================================================== + * FindTask: Return a pointer to the named task. + *============================================================================*/ +TaskRecord_t * AddressBook::FindTask(const std::string &TaskName) +{ + // Check that the task exists. + TaskMap_t::iterator TSearch = TaskMap.find(TaskName); + if (TSearch == TaskMap.end()) { + ERETURN("Task Does Not Exist", PNULL); + } + + return TSearch->second; +} + + +unsigned AddressBook::MapDevice(TaskRecord_t *TRec, Record_t *DRec) +{ + // Add device to name map + TRec->NameMap.insert(NameMap_t::value_type(DRec->Name, DRec)); + + // Add the device to the correct DeviceType list. + TRec->DevTypes[DRec->DeviceType].second.push_back(DRec); + + if(DRec->RecordType == DeviceExt) // Device has an external conn + { + TRec->ExtCon.push_back(DRec); // Add it to the list of such devices. + } + + // Add the device to the correct attribute list + if(DRec->Attribute > -1) + { + TRec->AttrTypes[static_cast(DRec->Attribute)].second.push_back(DRec); + } + + return SUCCESS; +} + +unsigned AddressBook::LinkDevice(TaskRecord_t *TRec, Record_t *DRec) +{ + // Add Device to Address map. + TRec->AddrMap.insert(AddrMap_t::value_type(DRec->Address, DRec)); + + // Add the device to the correct Supervisor list. + // Supervisors may be loaded after devices, may need to build the map. + SupMap_t::iterator SSearch = TRec->SupMap.find(DRec->Supervisor); + if (SSearch == TRec->SupMap.end()) { //Supervisor is NOT in the map yet. + std::pair LastVal; // Have to do this because C++98 does not like initialiser lists... + LastVal = TRec->SupMap.insert(SupMap_t::value_type(DRec->Supervisor, RecordVect_t())); + LastVal.first->second.push_back(DRec); // RecordVect_t({DRec}) in the insert would be neater and not need the pair. + + } else { //Supervisor is already in the map. + SSearch->second.push_back(DRec); + } + + return SUCCESS; +} + + +/*============================================================================== + * ValidateTask: Check that the task has a valid vector of DeviceTypes. + *============================================================================*/ +unsigned AddressBook::ValidateTask(TaskData_t &Data) +{ + for(std::vector::const_iterator D=Data.DeviceTypes.begin(); + D!=Data.DeviceTypes.end(); D++) + { + if (unsigned retVal = ValidateDeviceType(*D, Data.MessageTypes.size())) + return retVal; + } + + return SUCCESS; +} + +/*============================================================================== + * ValidateDeviceType: Check that DeviceType Message indices are valid and not + * out of range. + * Added and separated from ValidateTask 5 July 2019 ADR + *============================================================================*/ +unsigned AddressBook::ValidateDeviceType(const DevTypeRecord_t& DevTypRec, unsigned MaxIdx) +{ + for(std::vector::const_iterator I=DevTypRec.InMsgs.begin(); + I!=DevTypRec.InMsgs.end(); I++) + { + if(*I >= MaxIdx) + { + ERETURN("Invalid MessageType Index", ERR_INVALID_MESSAGE_TYPE); + } + } + + // changed to iterate over OuMsgs (was InMsgs) 5 July 2019 ADR + for(std::vector::const_iterator O=DevTypRec.OuMsgs.begin(); + O!=DevTypRec.OuMsgs.end(); O++) + { + if(*O >= MaxIdx) + { + ERETURN("Invalid MessageType Index", ERR_INVALID_MESSAGE_TYPE); + } + } + return 0; +} + + +/*============================================================================== + * ValidateDevice: Check that the device does not already exist in the name map + * and that indices in the Record are not out of range. + *============================================================================*/ +unsigned AddressBook::ValidateDevice(TaskRecord_t *TRec, Record_t &DevRec) +{ + // Check that the device does not already exist in the maps + AddrMap_t::const_iterator DASearch = TRec->AddrMap.find(DevRec.Address); + if (DASearch != TRec->AddrMap.end()) { + ERETURN("DUPADDR: Device Address already added", ERR_DEVICE_ADDR_USED); + } + + NameMap_t::const_iterator DNSearch = TRec->NameMap.find(DevRec.Name); + if (DNSearch != TRec->NameMap.end()) { + ERETURN("DUPNAME: Device Name already added", ERR_DEVICENAME_USED); + } + + // Check that the indices are valid BEFORE adding the Device. + if (DevRec.DeviceType >= TRec->DevTypes.size()) { + ERETURN("Invalid DeviceType Index", ERR_INVALID_DEVTYPE); + } + + if (DevRec.Attribute >= static_cast(TRec->AttrTypes.size())) { + ERETURN("Invalid AttributeType Index", ERR_INVALID_ATTRIBUTE); + } + + return SUCCESS; // Validation Successful +} + +/*============================================================================== + * CheckVector: Check that adding a Record to the vector won't cause a + * reallocation as this would silently invalidate all of the maps. + * If the vector is at capacity, increase it's size, clear the task + * linkage & mappings and mark both as dirty. + * + * As the Record vectors are pre-sized based on what should be in + * the task plus one element, there should never be a reallocation. + *============================================================================*/ +unsigned AddressBook::CheckVector(TaskRecord_t *TRec, std::vector &Vect) +{ + if(Vect.size() == Vect.capacity()) + { + Vect.reserve(static_cast(Vect.capacity()*1.25)); + ClearLink(TRec); + ClearMap(TRec); + return ERR_INVALID_MAP; + } + return SUCCESS; +} + + +/*============================================================================== + * ClearMap: Clear the Device to DeviceType/Attribute/(Message) Type links + * and mark the linkage as dirty. Also clears the Name>Record map + * and list of records with external connections. + *============================================================================*/ +unsigned AddressBook::ClearMap(TaskRecord_t *TRec) +{ + TRec->MapValid = false; // Mark the map as dirty. + TRec->NameMap.clear(); // Clear the Name map + TRec->ExtCon.clear(); // Clear External Connections List. + + // Clear DevType->DeviceRecord vectors + for(std::vector::iterator D=TRec->DevTypes.begin(); + D!=TRec->DevTypes.end();D++) + { + D->second.clear(); + } + + // Clear Attribute->DeviceRecord vectors + for(std::vector::iterator A=TRec->AttrTypes.begin(); + A!=TRec->AttrTypes.end();A++) + { + A->second.clear(); + } + + return SUCCESS; +} + + +/*============================================================================== + * ClearLink: Clear the Address>Record map and the Supervisor>Device links. + * Also marks the linkage as dirty. + *============================================================================*/ +unsigned AddressBook::ClearLink(TaskRecord_t *TRec) +{ + TRec->LinkValid = false; // Mark the linkage as dirty. + TRec->AddrMap.clear(); // Clear the Addres>DeviceRecord map. + + //Clear the Device Record vectors for each supervisor. + for(SupMap_t::iterator S=TRec->SupMap.begin();S!=TRec->SupMap.end();S++) + { + S->second.clear(); + } + + return SUCCESS; +} + + +//============================================================================== +//Integrity Checks +unsigned AddressBook::IntegCheck(void) +{ + std::vector Temp; + return IntegCheck(Temp); +} + +unsigned AddressBook::IntegCheck(std::vector &TaskIntegs) +{ + unsigned TInteg, Ret = 0; + + for(TaskMap_t::const_iterator T=TaskMap.begin(); + T!=TaskMap.end();T++) + { + TInteg = T->second->Integrity(); + TaskIntegs.push_back(TInteg); + if(TInteg) Ret++; + } + + return Ret; +} + +unsigned AddressBook::IntegTask(std::string &TaskName, bool Verbose, FILE * fp) +{ + TaskRecord_t *TRec = FindTask(TaskName); + if(TRec == PNULL) + { + ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); + } + + return TRec->Integrity(Verbose, fp); +} + + + +//============================================================================== +//Dumps + +void AddressBook::Dump(std::string &Name) +{ + Dump(stdout, Name); +} + +void AddressBook::Dump(FILE * fp, std::string Name) +{ + fprintf(fp,"AddressBook+++++++++++++++++++++++++++++++++\n"); + fprintf(fp,"ABderived (this derived process) : %s\n",ABderived.c_str()); + + if(Name == std::string("")) // Dump high-level task data. + { + fprintf(fp,"Total number of tasks: %d:\n", TaskCount); + for(TaskMap_t::iterator T=TaskMap.begin(); + T!=TaskMap.end();T++) + { + TaskRecord_t *TRec = T->second; + fprintf(fp,"Task %ld: Key: %s At: %" PTR_FMT "\n", + static_cast(std::distance(TaskMap.begin(), T)), + T->first.c_str(), reinterpret_cast(TRec)); + fprintf(fp,"\tName: %s\n", TRec->Name.c_str()); + fprintf(fp,"\tPath: %s\n", TRec->Path.c_str()); + fprintf(fp,"\tXML: %s\n", TRec->XML.c_str()); + fprintf(fp,"\tExecPath: %s\n", TRec->ExecPath.c_str()); + fprintf(fp,"\tExternal Count: %ld\n", TRec->ExtCntMax); + fprintf(fp,"\tDevice Count: %ld\n", TRec->DevCntMax); + fprintf(fp,"\tState: %d\n", TRec->State); + fprintf(fp,"\tAllocated Supervisors: %ld\n", TRec->SupCnt); + fprintf(fp,"\tLoaded Externals: %ld\n", TRec->ExtCnt); + fprintf(fp,"\tLoaded Devices: %ld\n", TRec->DevCnt); + } + } else { // Dump task-specific data. + // Check that the task exists. + TaskMap_t::const_iterator Search = TaskMap.find(Name); + if (Search == TaskMap.end()) { + fprintf(fp,"ERROR: Task %s not found.\n", Name.c_str()); + } else { + TaskRecord_t *TRec = Search->second; + + //Dump Task Data + fprintf(fp,"Task: %s at %" PTR_FMT "\n", Search->first.c_str(), reinterpret_cast(TRec)); + fprintf(fp,"\tPath: %s\n", TRec->Path.c_str()); + fprintf(fp,"\tExecPath: %s\n", TRec->ExecPath.c_str()); + fprintf(fp,"\tExternal Count: %ld\n", TRec->ExtCntMax); + fprintf(fp,"\tDevice Count: %ld\n", TRec->DevCntMax); + fprintf(fp,"\tState: %d\tTaskValid: %d\tMap Valid: %d\tLink Valid: %d\n", + TRec->State, TRec->TaskValid, TRec->MapValid, TRec->LinkValid); + + //Dump Message Types + fprintf(fp,"\tMessage Types:\t%lu\n", static_cast(TRec->MsgTypes.size())); + fprintf(fp,"\t\tIdx\tName\tMapIdx\tMapName\tDTypeI\tDTypeO\n"); + for(std::vector::const_iterator M + =TRec->MsgTypes.begin();M!=TRec->MsgTypes.end();M++) + { + fprintf(fp,"\t\t%ld\t%s\t", static_cast(M-TRec->MsgTypes.begin()), M->Name.c_str()); + + IdxMap_t::const_iterator MSearch = TRec->MsgTypeMap.find(M->Name); + if (MSearch == TRec->MsgTypeMap.end()) { + fprintf(fp,"Not Mapped\t"); + } else { + fprintf(fp,"%d\t%s\t", MSearch->second, + TRec->MsgTypes[MSearch->second].Name.c_str()); + } + + for(std::vector::const_iterator I= + M->Inputs.begin(); I!=M->Inputs.end(); I++) + { // Input Messages + fprintf(fp,"%d,",*I); + } + fprintf(fp,"\t"); + + for(std::vector::const_iterator O= + M->Outputs.begin();O!=M->Outputs.end(); O++) + { // Output Messages + fprintf(fp,"%d,",*O); + } + fprintf(fp,"\n"); + } + fprintf(fp,"\n"); + + //Dump Device Types + fprintf(fp,"\tDevice Types:\t%lu\n", static_cast(TRec->DevTypes.size())); + fprintf(fp,"\t\tIdx\tName\tMapIdx\tMapName\tDevCnt\tInMsgs\tOuMsgs\n"); + for(std::vector::const_iterator D + =TRec->DevTypes.begin();D!=TRec->DevTypes.end();D++) + { + fprintf(fp,"\t\t%ld\t%s\t", static_cast(D-TRec->DevTypes.begin()), D->first.Name.c_str()); + + //Print the DevTypeMap mapping + IdxMap_t::const_iterator DSearch = TRec->DevTypeMap.find(D->first.Name); + if (DSearch == TRec->DevTypeMap.end()) { + fprintf(fp,"Not Mapped\t"); + } else { + fprintf(fp,"%d\t%s\t", DSearch->second, + TRec->DevTypes[DSearch->second].first.Name.c_str()); + } + + //Print the number of devices of this type. + fprintf(fp,"%lu\t", static_cast(D->second.size())); + + //Print the Input Msg Indices + for(std::vector::const_iterator I + =D->first.InMsgs.begin();I!=D->first.InMsgs.end();I++) + { + fprintf(fp,"%d,", *I); + } + fprintf(fp,"\t"); + + //Print the Output Msg Indices + for(std::vector::const_iterator O + =D->first.OuMsgs.begin();O!=D->first.OuMsgs.end();O++) + { + fprintf(fp,"%d,", *O); + } + fprintf(fp,"\n"); + + } + fprintf(fp,"\n"); + + //Dump Attribute Types + fprintf(fp,"\tAttribute Types:\t%lu\n", static_cast(TRec->AttrTypes.size())); + if(TRec->AttrTypes.size()>0) + { + fprintf(fp,"\t\tIdx\tName\tMapIdx\tMapName\tDevCnt\n"); + for(std::vector::const_iterator A + =TRec->AttrTypes.begin();A!=TRec->AttrTypes.end();A++) + { + fprintf(fp,"\t\t%ld\t%s\t", static_cast(A-TRec->AttrTypes.begin()), + A->first.c_str()); + + IdxMap_t::const_iterator ASearch = TRec->AttrTypeMap.find(A->first); + if (ASearch == TRec->AttrTypeMap.end()) { + fprintf(fp,"Not Mapped\t"); + } else { + fprintf(fp,"%d\t%s\t", ASearch->second, + TRec->AttrTypes[ASearch->second].first.c_str()); + } + + //Print the number of devices with this attribute. + fprintf(fp,"%ld\n", static_cast(A->second.size())); + } + fprintf(fp,"\n"); + } + + // Dump allocated Supervisors + fprintf(fp,"\tAllocated Supervisors: %ld\n", TRec->SupCnt); + if(TRec->SupCnt>0) + { + fprintf(fp,"\t\tIdx\tAddress\tRank\tDev Count\n"); + for(std::vector::const_iterator R + =TRec->Supervisors.begin();R!=TRec->Supervisors.end();R++) + { + //TODO: Write the map search to get count of devices for + // a supervisor. + fprintf(fp,"\t\t%ld\t%" SYMA_FMT "\t%lu\t%d\n", + static_cast(R-TRec->Supervisors.begin()), + R->Address, R->Rank, 0); + } + fprintf(fp,"\n"); + } + + // Dump loaded Externals + fprintf(fp,"\tLoaded Externals: %ld\n", TRec->ExtCnt); + if(TRec->ExtCnt>0) + { + fprintf(fp,"\t\tIdx\tName\tAddress\n"); + for(std::vector::const_iterator R + =TRec->Externals.begin();R!=TRec->Externals.end();R++) + { + fprintf(fp,"\t\t%ld\t%s\t%" SYMA_FMT "\n", + static_cast(R-TRec->Externals.begin()), + R->Name.c_str(), R->Address); + } + fprintf(fp,"\n"); + } + + + // Dump loaded Devices + fprintf(fp,"\tLoaded Devices: %ld\n", TRec->DevCnt); + if(TRec->DevCnt>0) + { + fprintf(fp,"\t\tIdx\tName\tAddress\t\tSupervisor\n"); + for(std::vector::const_iterator R + =TRec->Devices.begin();R!=TRec->Devices.end();R++) + { + fprintf(fp,"\t\t%ld\t%s\t%" SYMA_FMT "\t%" SYMA_FMT "\n", + static_cast(R-TRec->Devices.begin()), + R->Name.c_str(), R->Address, R->Supervisor); + } + } + } + } + + fprintf(fp,"AddressBook---------------------------------\n"); + fflush(fp); +} +} /* namespace AddressBookNS */ diff --git a/Source/NameServer/AddressBook.hpp b/Source/NameServer/AddressBook.hpp new file mode 100644 index 00000000..cc27b3cd --- /dev/null +++ b/Source/NameServer/AddressBook.hpp @@ -0,0 +1,162 @@ +/*============================================================================== + * AddressBook: A back-end implementation for the POETS sbase & Nameserver + * + * Tasks can be *maped* and *linked*: + * Mapped: Device Records connected to Device (and Message) and Attribute + * types. Device Records with External Connections also listed. + * Linked: Device Records connected to Addresses in Address Map and listed + * in the relevant Supervisor vector. + * + * Linkage is invalidated when/if a Device Record is updated after load. + * Mapping is invalidated if more devices than expected are loaded. + * + * Struct sizing with no Pragma Packing: + * Win 64-Bit Win 32-Bit Mac 64-Bit + * TaskRecord_t 560 324 464 + * Record_t 72 64 52 + * TaskData_t 280 184 216 + * DeviceData_t 56 48 40 + * + * Memory Footprint* 221.7MB 153.2MB 194MB + * *1 task with 1 million devices, 1 supervisor and 1 external. + * + * + * + *============================================================================*/ + +#ifndef AddressBook_H +#define AddressBook_H + +#include "OSFixes.hpp" + +#include "ABDefs.hpp" +#include "ABTask.hpp" + +#include +#include +#include +#include +#include +#include +#include + + + +#define MAXSUPERVISORS 16 + +//============================================================================== +namespace AddressBookNS // changed name to avoid conflicts with the class itself + // 14 July 2019 ADR +{ +typedef std::map TaskMap_t; + +class AddressBook +{ +public: + AddressBook(std::string d); + ~AddressBook(); + + unsigned GetTaskCount(void); + + // Task Manipulation + unsigned AddTask(const std::string &TaskName, TaskData_t &Data); + unsigned ListTask(std::vector &Tasks); + unsigned GetTask(const std::string &TaskName, TaskData_t &Task); + unsigned DelTask(const std::string &TaskName); + + unsigned AddDeviceType(const std::string &TaskName, const DevTypeRecord_t &DeviceType); // add a new DeviceType + unsigned ClearTask(const std::string &TaskName); // deletes all the devices for a task without removing the task. + + TaskState_t TaskState(std::string &TaskName); // Get the task state + unsigned TaskState(std::string &TaskName, TaskState_t State); // Set the task state + + std::string TaskExecPath(std::string &TaskName); // Get the path of a task + unsigned TaskExecPath(std::string &TaskName, std::string &NewPath); // Set the path of a task + + bool TaskValid(std::string &TaskName); + bool TaskMapValid(std::string &TaskName); + bool TaskLinkValid(std::string &TaskName); + + unsigned RebuildTask(std::string &TaskName); + unsigned BuildMaps(std::string &TaskName); + unsigned BuildLink(std::string &TaskName); + + // Device Manipulation + unsigned AddDevice(std::string &TaskName, Record_t &Device, bool Validate = true); + unsigned UpdateDevice(std::string &TaskName, DeviceData_t &Data); + + // Find a device + unsigned FindDevice(std::string &TaskName, std::string &Name, const Record_t* &DRec); + unsigned FindDevice(std::string &TaskName, SymAddr_t Address, const Record_t* &DRec); + + // Find all devices supervised by X + unsigned FindBySuper(std::string &TaskName, SymAddr_t Supervisor, const RecordVect_t* &Records); + + unsigned FindByType(std::string &TaskName, std::string Type, const RecordVect_t* &Records); + unsigned FindByAttribute(std::string &TaskName, std::string Attribute, const RecordVect_t* &Records); + + unsigned FindByInMsg(std::string &TaskName, std::string Msg, const RecordVect_t* &Records); //TODO + unsigned FindByOuMsg(std::string &TaskName, std::string Msg, const RecordVect_t* &Records); //TODO + + unsigned GetDevices(std::string &TaskName, const std::vector* &Records); + unsigned GetExternals(std::string &TaskName, const std::vector* &Records); + unsigned GetSupervisors(std::string &TaskName, const std::vector* &Records); + unsigned GetExtCon(std::string &TaskName, const RecordVect_t* &Records); + + // Device Count getters + long GetDeviceCount(std::string &TaskName); + long GetLoadedDeviceCount(std::string &TaskName); + long GetExternalCount(std::string &TaskName); + long GetLoadedExternalCount(std::string &TaskName); + int GetSupervisorCount(std::string &TaskName); + + // Integrity + unsigned IntegCheck(void); // Integrity check of ALL tasks + unsigned IntegCheck(std::vector &TaskIntegs); + unsigned IntegTask(std::string &TaskName, bool Verbose = false, FILE * = stdout); // Ingerity Check a Task by name + //unsigned IntegTask(const TaskRecord_t *TRec, bool Verbose = false, FILE * = stdout); // Ingerity Check a Task by pointer + + // Debugging + void Dump(std::string &Name); + void Dump(FILE * = stdout, std::string Name = ""); + + static const long ERR_BAD_COUNT = -1; + + static const unsigned SUCCESS = 0; + static const unsigned ERR_INVALID_TASK = 1; + static const unsigned ERR_INVALID_DEVTYPE = 2; + static const unsigned ERR_INVALID_DEVICE = 3; + static const unsigned ERR_INVALID_MESSAGE_TYPE = 4; + static const unsigned ERR_INVALID_ATTRIBUTE = 5; + static const unsigned ERR_DEVICE_DATA_MISMATCH = 6; + static const unsigned ERR_TASKNAME_USED = 7; + static const unsigned ERR_DEVICENAME_USED = 8; + static const unsigned ERR_DEVICE_ADDR_USED = 9; + static const unsigned ERR_TASK_NOT_FOUND = 10; + static const unsigned ERR_DEVICE_NOT_FOUND = 11; + static const unsigned ERR_INVALID_MAP = 12; + static const unsigned ERR_INVALID_SUPERVISOR = 13; + +private: + TaskMap_t TaskMap; + int TaskCount; + std::string ABderived; // changed to allow multiple inheritance (from CommonBase) 14 July 2019 ADR + + TaskRecord_t * FindTask(const std::string &TaskName); + + unsigned ValidateDevice(TaskRecord_t *TRec, Record_t &DevRec); + unsigned ValidateDeviceType(const DevTypeRecord_t& DevTypRec, unsigned MaxIdx); + unsigned ValidateTask(TaskData_t &Data); + + unsigned CheckVector(TaskRecord_t *TRec, std::vector &Vect); + + unsigned LinkDevice(TaskRecord_t *TRec, Record_t *DRec); // Add Device to name map & supervisor list. + unsigned MapDevice(TaskRecord_t *TRec, Record_t *DRec); // Add Device to other maps & lists. + + unsigned ClearMap(TaskRecord_t *TRec); + unsigned ClearLink(TaskRecord_t *TRec); + +}; +} /* namespace AddressBookNS */ + +#endif /* AddressBook_H */ diff --git a/Source/NameServer/NameServer.cpp b/Source/NameServer/NameServer.cpp index e577ad56..03a09a9b 100644 --- a/Source/NameServer/NameServer.cpp +++ b/Source/NameServer/NameServer.cpp @@ -1,29 +1,31 @@ //------------------------------------------------------------------------------ #include "NameServer.h" -#include "CommonBase.h" #include "PMsg_p.hpp" #include "mpi.h" #include "Pglobals.h" -#include "Ns_el.h" -#include "jnj.h" +// #include "Ns_el.h" +// #include "jnj.h" #include //============================================================================== NameServer::NameServer(int argc,char * argv[],string d): - CommonBase(argc,argv,d,string(__FILE__)) + SBase(argc,argv,d,string(__FILE__)) { + FnMapx.push_back(new FnMap_t); // create a default function table // Load the message map -FnMapx[PMsg_p::KEY(Q::CANDC,Q::CLEAR)] = &NameServer::OnClr; -FnMapx[PMsg_p::KEY(Q::CANDC,Q::DUMP )] = &NameServer::OnDump; -FnMapx[PMsg_p::KEY(Q::TASK, Q::FWD )] = &NameServer::OnFwd; -FnMapx[PMsg_p::KEY(Q::CANDC,Q::LOAD )] = &NameServer::OnLoad; -FnMapx[PMsg_p::KEY(Q::CANDC,Q::LOG )] = &NameServer::OnLog; -FnMapx[PMsg_p::KEY(Q::CANDC,Q::MONI )] = &NameServer::OnMoni; -FnMapx[PMsg_p::KEY(Q::TASK, Q::QUERY)] = &NameServer::OnQuery; - -MPISpinner(); // Spin on MPI messages; exit only on DIE + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &NameServer::OnCfg; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &NameServer::OnCfg; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &NameServer::OnCfg; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &NameServer::OnCfg; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &NameServer::OnCfg; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &NameServer::OnCfg; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL)] = &NameServer::OnDump; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &NameServer::OnDump; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &NameServer::OnDump; + + MPISpinner(); // Spin on MPI messages; exit only on DIE //printf("********* NameServer rank %d on the way out\n",Urank); fflush(stdout); } @@ -37,94 +39,146 @@ NameServer::~NameServer() //------------------------------------------------------------------------------ -void NameServer::Dump(FILE * fp) +void NameServer::Dump(FILE * fp, string task) { -fprintf(fp,"NameServer dump+++++++++++++++++++++++++++++++++++\n"); - -fprintf(fp,"NameServer dump-----------------------------------\n"); +if (task.empty()) +{ + fprintf(fp,"NameServer summary dump+++++++++++++++++++++++++++\n"); + SBase::Dump(fp); + fprintf(fp,"NameServer summary dump---------------------------\n"); +} +else +{ + fprintf(fp,"NameServer dump+++++++++++++++++++++++++++++++++++\n"); + SBase::Dump(fp,task); + fprintf(fp,"NameServer dump-----------------------------------\n"); +} CommonBase::Dump(fp); } //------------------------------------------------------------------------------ -void NameServer::Load1(Ns_0 * p0) +unsigned NameServer::OnCfg(PMsg_p * msg, unsigned comm) { -static unsigned cnt=0; -printf("\n********** %u\n\n",cnt++); -p0->Dump(); + switch(msg->L(2)) + { + case Q::DIST: + return ConfigDistribute(msg, comm); + case Q::TDIR: + return ConfigDir(msg, comm); + case Q::BLD: + return SBase::ConfigBuild(msg, comm); + case Q::RECL: + return ConfigRecall(msg, comm); + case Q::DEL: + return SBase::ConfigDelete(msg, comm); + case Q::STATE: + return ConfigState(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } } //------------------------------------------------------------------------------ -unsigned NameServer::OnClr(PMsg_p *) +unsigned NameServer::OnDump(PMsg_p *msg, unsigned comm) { - - -return 0; + if (msg->L(2) == Q::LIST) return DumpSummary(msg); + if (msg->L(2) != Q::TASK) + { + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } + switch(msg->L(3)) + { + case Q::ALL: + return DumpAll(msg); + case Q::NM: + return DumpTask(msg); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } } //------------------------------------------------------------------------------ -unsigned NameServer::OnDump(PMsg_p *) +unsigned NameServer::ConfigDir(PMsg_p * msg, unsigned comm) { - - -return 0; + return 0; } -//------------------------------------------------------------------------------ - -unsigned NameServer::OnFwd(PMsg_p *) +unsigned NameServer::ConfigDistribute(PMsg_p * msg, unsigned comm) { - - -return 0; + return 0; } -//------------------------------------------------------------------------------ +unsigned NameServer::ConfigRecall(PMsg_p * msg, unsigned comm) +{ + return 0; +} -unsigned NameServer::OnLoad(PMsg_p * pZ) -// Load some fraction of the NameServer datastructure with the entities supplied -// in the message. +unsigned NameServer::ConfigState(PMsg_p * msg, unsigned comm) { -Ns_el * pN = new Ns_el(pZ); // Unwrap the message -int cnt=0; -unsigned * ploc = pN->Get(0,cnt);// Get hold of the key vector -if (ploc==0) Post(909); -vector * pV = pN->Construct(); // Expose the entities -WALKVECTOR(Ns_0 *,(*pV),i) Load1(*i); // Add to database -delete pN; -return 0; + return 0; } //------------------------------------------------------------------------------ -unsigned NameServer::OnLog(PMsg_p *) +unsigned NameServer::DumpAll(PMsg_p *msg) { - - -return 0; + // filename must be treated as an absolute literal, because tasks could in + // general have different paths, therefore there is no way to infer what + // additional path string might be appended to the file name give. + string filename = msg->Zname(1); + FILE* dumpFile; + if (filename.empty()) dumpFile = stdout; + else dumpFile = fopen(filename.c_str(), "a"); + vector tasks; + unsigned err = AddressBook::SUCCESS; + if (err = ListTask(tasks)) + { + Post(709, int2str(Urank)); + if (dumpFile != stdout) fclose(dumpFile); + return err; + } + WALKVECTOR(string, tasks, task) Dump(dumpFile, *task); + return err; } //------------------------------------------------------------------------------ -unsigned NameServer::OnMoni(PMsg_p *) +unsigned NameServer::DumpSummary(PMsg_p *msg) { - - -return 0; + string filename = msg->Zname(1); + FILE* dumpFile; + if (filename.empty()) dumpFile = stdout; + else dumpFile = fopen(filename.c_str(), "a"); + Dump(dumpFile); + if (dumpFile != stdout) fclose(dumpFile); + return AddressBook::SUCCESS; } //------------------------------------------------------------------------------ -unsigned NameServer::OnQuery(PMsg_p *) +unsigned NameServer::DumpTask(PMsg_p *msg) { - - -return 0; + string taskName = msg->Zname(0); + if (taskName.empty()) + { + Post(710, int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + string filename = msg->Zname(1); + FILE* dumpFile; + if (filename.empty()) dumpFile = stdout; + else dumpFile = fopen(filename.c_str(), "a"); + Dump(dumpFile, taskName); + if (dumpFile != stdout) fclose(dumpFile); + return AddressBook::SUCCESS; } -//------------------------------------------------------------------------------ //============================================================================== diff --git a/Source/NameServer/NameServer.h b/Source/NameServer/NameServer.h index 7b957495..1c4ba6a8 100644 --- a/Source/NameServer/NameServer.h +++ b/Source/NameServer/NameServer.h @@ -1,32 +1,36 @@ #ifndef __NameServerH__H #define __NameServerH__H -#include "CommonBase.h" -#include "Ns_el.h" +#include "SBase.h" +// #include "Ns_el.h" //============================================================================== -class NameServer : public CommonBase +class NameServer : public SBase { public: NameServer(int,char **,string); virtual ~ NameServer(); + unsigned OnCfg (PMsg_p *, unsigned); + unsigned OnDump (PMsg_p *, unsigned); + private: -#include "Decode.cpp" -void Dump(FILE * = stdout); -void Load1(Ns_0 *); -unsigned OnClr (PMsg_p *); -unsigned OnDump (PMsg_p *); -unsigned OnFwd (PMsg_p *); -unsigned OnLoad (PMsg_p *); -unsigned OnLog (PMsg_p *); -unsigned OnMoni (PMsg_p *); -unsigned OnQuery(PMsg_p *); - -typedef unsigned (NameServer::*pMeth)(PMsg_p *); -map FnMapx; +#include "SDecode.cpp" + void Dump(FILE * = stdout, string s = ""); + + unsigned ConfigDir (PMsg_p *, unsigned); + unsigned ConfigDistribute (PMsg_p *, unsigned); + unsigned ConfigRecall (PMsg_p *, unsigned); + unsigned ConfigState (PMsg_p *, unsigned); + unsigned DumpAll (PMsg_p *); + unsigned DumpSummary (PMsg_p *); + unsigned DumpTask (PMsg_p *); + +typedef unsigned (NameServer::*pMeth)(PMsg_p *, unsigned); +typedef map FnMap_t; +vector FnMapx; }; diff --git a/Source/NameServer/SBase.cpp b/Source/NameServer/SBase.cpp new file mode 100644 index 00000000..5cf9b8d6 --- /dev/null +++ b/Source/NameServer/SBase.cpp @@ -0,0 +1,726 @@ +#include "SBase.h" +#include "Pglobals.h" +#include // debug + +SBase::SBase(int argc,char ** argv,string d,string s): CommonBase(argc,argv,d,s), AddressBook(d) +{ + FnMapx.push_back(new FnMap_t); // create a default function table (for the *nameserver base* class!) + // Load event handler map + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NM )] = &SBase::OnSend; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ID )] = &SBase::OnSend; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ALL )] = &SBase::OnSend; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NGRP)] = &SBase::OnSend; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::IGRP)] = &SBase::OnSend; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NSUP)] = &SBase::OnSend; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ISUP)] = &SBase::OnSend; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::SUPV )] = &SBase::OnSend; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::EXTN )] = &SBase::OnSend; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::NM )] = &SBase::OnSend; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::IN )] = &SBase::OnSend; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::OUT )] = &SBase::OnSend; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::ATTR )] = &SBase::OnSend; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NM )] = &SBase::OnQuery; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ID )] = &SBase::OnQuery; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ALL )] = &SBase::OnQuery; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NGRP )] = &SBase::OnQuery; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::IGRP )] = &SBase::OnQuery; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NSUP )] = &SBase::OnQuery; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ISUP )] = &SBase::OnQuery; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::SUPV )] = &SBase::OnQuery; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::EXTN )] = &SBase::OnQuery; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::NM )] = &SBase::OnQuery; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::IN )] = &SBase::OnQuery; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::OUT )] = &SBase::OnQuery; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::ATTR )] = &SBase::OnQuery; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &SBase::OnCfg; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &SBase::OnCfg; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &SBase::OnCfg; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &SBase::OnCfg; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &SBase::OnCfg; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &SBase::OnCfg; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NF )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::LIST )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::TASK )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::TASK,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DATA,Q::TASK )] = &SBase::OnData; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DATA,Q::DEVT )] = &SBase::OnData; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DATA,Q::DEVI )] = &SBase::OnData; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DATA,Q::DEVE )] = &SBase::OnData; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DATA,Q::EXTN )] = &SBase::OnData; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DATA,Q::SUPV )] = &SBase::OnData; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::ON )] = &SBase::OnCmd; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::OFF )] = &SBase::OnCmd; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::ON )] = &SBase::OnCmd; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::OFF )] = &SBase::OnCmd; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &SBase::OnDump; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &SBase::OnDump; + (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &SBase::OnDump; +} + +SBase::~SBase() +{ +} + +unsigned SBase::OnCfg(PMsg_p *msg, unsigned comm) +{ + switch(msg->L(2)) + { + case Q::DIST: + return ConfigDistribute(msg, comm); + case Q::TDIR: + return ConfigDir(msg, comm); + case Q::BLD: + return ConfigBuild(msg, comm); + case Q::RECL: + return ConfigRecall(msg, comm); + case Q::DEL: + return ConfigDelete(msg, comm); + case Q::STATE: + return ConfigState(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +unsigned SBase::OnCmd(PMsg_p *msg, unsigned comm) +{ + switch(msg->L(2)) + { + case Q::INTG: + return CmdIntegrity(msg); + case Q::MONI: + return 0; + case Q::LOG: + return 0; + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +unsigned SBase::OnData(PMsg_p *msg, unsigned comm) +{ + switch(msg->L(2)) + { + case Q::TASK: + return DataTask(msg); + case Q::DEVT: + return DataDevType(msg); + case Q::DEVI: + return DataDevice(msg); + case Q::DEVE: + return DataDeviceExternal(msg); + case Q::EXTN: + return DataExternal(msg); + case Q::SUPV: + return DataSupervisor(msg); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +unsigned SBase::OnDump(PMsg_p *msg, unsigned comm) +{ + if (msg->L(2) == Q::LIST) return DumpSummary(msg); + if (msg->L(2) != Q::TASK) + { + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } + switch(msg->L(3)) + { + case Q::ALL: + return DumpAll(msg); + case Q::NM: + return DumpTask(msg); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +unsigned SBase::OnQuery(PMsg_p *msg, unsigned comm) +{ + switch(msg->L(2)) + { + case Q::DEVI: + switch(msg->L(3)) + { + case Q::NM: + case Q::NGRP: + case Q::NSUP: + return QueryDevIByName(msg, comm); + case Q::ID: + case Q::IGRP: + case Q::ISUP: + return QueryDevIByID(msg, comm); + case Q::ALL: + return QueryDevIAll(msg, comm); + default: + Post(702,uint2str(static_cast(msg->L(3))),int2str(Urank)); + return 0; + } + case Q::SUPV: + return QuerySupv(msg, comm); + case Q::EXTN: + return QueryExtn(msg, comm); + case Q::DEVT: + return QueryDevT(msg, comm); + case Q::ATTR: + return QueryAttr(msg, comm); + case Q::LIST: + return QueryList(msg, comm); + case Q::TASK: + return QueryTask(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +unsigned SBase::OnReply(PMsg_p *msg, unsigned comm) +{ + Post(703,uint2str(msg->Key()),int2str(Urank)); + return 0; +} + +unsigned SBase::OnSend(PMsg_p *msg, unsigned comm) +{ + switch(msg->L(2)) + { + case Q::DEVI: + switch(msg->L(3)) + { + case Q::NM: + case Q::NGRP: + case Q::NSUP: + return SendDevIByName(msg, comm); + case Q::ID: + case Q::IGRP: + case Q::ISUP: + return SendDevIByID(msg, comm); + case Q::ALL: + return SendDevIAll(msg, comm); + default: + Post(702,uint2str(static_cast(msg->L(3))),int2str(Urank)); + return 0; + } + case Q::SUPV: + return SendSupv(msg, comm); + case Q::EXTN: + return SendExtn(msg, comm); + case Q::DEVT: + return SendDevT(msg, comm); + case Q::ATTR: + return SendAttr(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +unsigned SBase::Connect(string svc) +{ + unsigned connErr = MPI_SUCCESS; + if ((connErr = CommonBase::Connect(svc)) != MPI_SUCCESS) return connErr; + int fIdx=FnMapx.size()-1; + // Load event handler map for the new comm + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NM )] = &SBase::OnSend; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ID )] = &SBase::OnSend; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ALL )] = &SBase::OnSend; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NGRP)] = &SBase::OnSend; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::IGRP)] = &SBase::OnSend; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NSUP)] = &SBase::OnSend; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ISUP)] = &SBase::OnSend; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::SUPV )] = &SBase::OnSend; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::EXTN )] = &SBase::OnSend; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::NM )] = &SBase::OnSend; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::IN )] = &SBase::OnSend; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::OUT )] = &SBase::OnSend; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::ATTR )] = &SBase::OnSend; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NM )] = &SBase::OnQuery; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ID )] = &SBase::OnQuery; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ALL )] = &SBase::OnQuery; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NGRP )] = &SBase::OnQuery; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::IGRP )] = &SBase::OnQuery; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NSUP )] = &SBase::OnQuery; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ISUP )] = &SBase::OnQuery; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::SUPV )] = &SBase::OnQuery; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::EXTN )] = &SBase::OnQuery; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::NM )] = &SBase::OnQuery; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::IN )] = &SBase::OnQuery; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::OUT )] = &SBase::OnQuery; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::ATTR )] = &SBase::OnQuery; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &SBase::OnCfg; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &SBase::OnCfg; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &SBase::OnCfg; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &SBase::OnCfg; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &SBase::OnCfg; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &SBase::OnCfg; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NF )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::LIST )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::TASK )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::TASK,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DATA,Q::TASK )] = &SBase::OnData; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DATA,Q::DEVT )] = &SBase::OnData; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DATA,Q::DEVI )] = &SBase::OnData; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DATA,Q::DEVE )] = &SBase::OnData; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DATA,Q::EXTN )] = &SBase::OnData; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DATA,Q::SUPV )] = &SBase::OnData; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::ON )] = &SBase::OnCmd; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::OFF )] = &SBase::OnCmd; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::ON )] = &SBase::OnCmd; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::OFF )] = &SBase::OnCmd; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &SBase::OnDump; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &SBase::OnDump; + (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &SBase::OnDump; + return MPI_SUCCESS; +} + +unsigned SBase::CmdIntegrity(PMsg_p *msg) +{ + return 0; +} + +unsigned SBase::ConfigBuild(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::ConfigDelete(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::ConfigDistribute(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::ConfigDir(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::ConfigRecall(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::ConfigState(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::DataTask(PMsg_p *msg) +{ + TaskData_t task; + task.Name = msg->Zname(0); + task.Path = msg->Zname(1); + task.XML = msg->Zname(2); + if (task.Name == "") + { + Post(704, int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + msg->GetX(0, task.MessageTypes); + msg->GetX(1, task.AttributeTypes); + vector counts; + msg->Get(2, counts); + if (counts.size() != 3) Post(720,uint2str(counts.size()),"3",task.Name,int2str(Urank)); + else + { + task.DeviceCount = counts[0]; + task.ExternalCount = counts[1]; + task.SupervisorCount = counts[2]; + } + return AddTask(task.Name, task); +} + +unsigned SBase::DataDevType(PMsg_p *msg) +{ + DevTypeRecord_t deviceType; + string taskName = msg->Zname(0); + deviceType.Name = msg->Zname(2); + if (taskName == "") + { + Post(705, int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + if (deviceType.Name == "") + { + Post(706, taskName, int2str(Urank)); + return AddressBook::ERR_INVALID_DEVTYPE; + } + msg->Get(0, deviceType.InMsgs); + msg->Get(1, deviceType.OuMsgs); + return AddDeviceType(taskName, deviceType); +} + +unsigned SBase::DataDevice(PMsg_p *msg) +{ + // Post(715,int2str(Urank),int2str(msg->Src()),msg->Zname(0)); + string taskName = msg->Zname(0); + if (taskName == "") + { + Post(705, int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + TaskData_t task; + if (GetTask(taskName, task)) + { + Post(708,"records",taskName,int2str(Urank)); + return AddressBook::ERR_TASK_NOT_FOUND; + } + string devTypName = msg->Zname(2); + DTypeIdx devIdx = 0; + while ((task.DeviceTypes[devIdx].Name != devTypName) && ++devIdx < task.DeviceTypes.size()); + if (devIdx >= task.DeviceTypes.size()) + { + Post(718,taskName,int2str(Urank),devTypName); + return AddressBook::ERR_INVALID_DEVTYPE; + } + FILE* dbgSBase = fopen("Root2NS_SBase_devices.txt","a"); // DEBUG ONLY + vector devNames; + // vector devData; + vector devAddrs; + int numSupers = 1; // get method expects a *reference* to the count + SymAddr_t* devSuper = msg->Get(3,numSupers); + if (!devSuper) + { + // no supervisor found + Post(718,taskName,int2str(Urank)); + return ERR_INVALID_SUPERVISOR; + } + vector devAttrs; + msg->GetX(0, devNames); + // msg->Get(1, devData); + msg->Get(1, devAddrs); + if (devNames.size() != devAddrs.size()) + { + Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAddrs.size()),"address"); + return ERR_DEVICE_DATA_MISMATCH; + } + msg->Get(2, devAttrs); + if (devNames.size() != devAttrs.size()) + { + Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAttrs.size()),"attribute"); + return ERR_DEVICE_DATA_MISMATCH; + } + // if (devNames.size() != devData.size()) + // { + // Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devData.size())); + // return ERR_DEVICE_DATA_MISMATCH; + // } + fprintf(dbgSBase, "Nameserver received device info for %u devices\n", devNames.size()); + fprintf(dbgSBase, "\n"); + fprintf(dbgSBase, "________________________________________________________________________________\n"); + fprintf(dbgSBase, "\n"); + for (unsigned d = 0; d < devNames.size(); d++) + { + Record_t device(devNames[d], + devAddrs[d], + *devSuper, + devIdx, + RecordType_t(Device), + devAttrs[d]); + // Record_t device(devNames[d], + // devData[d].Address, + // devData[d].RecordType == Supervisor ? static_cast(devData[d].Rank) : devData[d].Supervisor, + // devData[d].DeviceType, + // devData[d].RecordType, + // devData[d].Attribute); + fprintf(dbgSBase, "Device %s:\n", device.Name.c_str()); + fprintf(dbgSBase, "Address: 0x%.16llx\n", device.Address); + if (device.RecordType == Supervisor) + fprintf(dbgSBase, "Supervisor Rank: %d\n", device.Rank); + else fprintf(dbgSBase, "Supervisor Address: 0x%.16llx\n", device.Supervisor); + fprintf(dbgSBase, "Device type index: %d\n", device.DeviceType); + fprintf(dbgSBase, "Attributes: %d\n", device.Attribute); + fprintf(dbgSBase, "Record type: %d\n", static_cast(device.RecordType)); + fprintf(dbgSBase, "\n"); + unsigned err = AddressBook::SUCCESS; + if ((err = AddDevice(taskName, device)) != AddressBook::SUCCESS) + { + /* + if (ClearTask(taskName) != AddressBook::SUCCESS) + { + Post(708, device.Name, taskName, int2str(Urank)); + return err; + } + */ + Post(707, device.Name, taskName, int2str(Urank)); + // return err; + } + } + fclose(dbgSBase); + return AddressBook::SUCCESS; +} + +// ----------------------------------------------------------------------------- + +unsigned SBase::DataDeviceExternal(PMsg_p *msg) +{ + return 0; +} + +// ----------------------------------------------------------------------------- + +unsigned SBase::DataExternal(PMsg_p *msg) +{ + return 0; +} + +// ----------------------------------------------------------------------------- + +unsigned SBase::DataSupervisor(PMsg_p *msg) +{ + // Post(715,int2str(Urank),int2str(msg->Src()),msg->Zname(0)); + string taskName = msg->Zname(0); + if (taskName == "") + { + Post(705, int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + TaskData_t task; + if (GetTask(taskName, task)) + { + Post(708,"records",taskName,int2str(Urank)); + return AddressBook::ERR_TASK_NOT_FOUND; + } + string devTypName = msg->Zname(2); + DTypeIdx devIdx = 0; + while ((task.DeviceTypes[devIdx].Name != devTypName) && ++devIdx < task.DeviceTypes.size()); + if (devIdx >= task.DeviceTypes.size()) + { + Post(718,taskName,int2str(Urank),devTypName); + return AddressBook::ERR_INVALID_DEVTYPE; + } + FILE* dbgSBase = fopen("Root2NS_SBase_devices.txt","a"); // DEBUG ONLY + vector devNames; + // vector devData; + vector devAddrs; + vector devRanks; + vector devAttrs; + msg->GetX(0, devNames); + // msg->Get(1, devData); + msg->Get(1, devAddrs); + if (devNames.size() != devAddrs.size()) + { + Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAddrs.size()),"address"); + return ERR_DEVICE_DATA_MISMATCH; + } + msg->Get(2, devAttrs); + if (devNames.size() != devAttrs.size()) + { + Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAttrs.size()),"attribute"); + return ERR_DEVICE_DATA_MISMATCH; + } + msg->Get(3, devRanks); + if (devNames.size() != devRanks.size()) + { + Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devRanks.size()),"rank"); + return ERR_DEVICE_DATA_MISMATCH; + } + // if (devNames.size() != devData.size()) + // { + // Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devData.size())); + // return ERR_DEVICE_DATA_MISMATCH; + // } + fprintf(dbgSBase, "Nameserver received supervisor info for %u supervisors\n", devNames.size()); + fprintf(dbgSBase, "\n"); + fprintf(dbgSBase, "________________________________________________________________________________\n"); + fprintf(dbgSBase, "\n"); + for (unsigned d = 0; d < devNames.size(); d++) + { + Record_t device(devNames[d], + devAddrs[d], + static_cast(devRanks[d]), + devIdx, + RecordType_t(Supervisor), + devAttrs[d]); + // Record_t device(devNames[d], + // devData[d].Address, + // devData[d].RecordType == Supervisor ? static_cast(devData[d].Rank) : devData[d].Supervisor, + // devData[d].DeviceType, + // devData[d].RecordType, + // devData[d].Attribute); + fprintf(dbgSBase, "Device %s:\n", device.Name.c_str()); + fprintf(dbgSBase, "Address: 0x%.16llx\n", device.Address); + if (device.RecordType == Supervisor) + fprintf(dbgSBase, "Supervisor Rank: %d\n", device.Rank); + else fprintf(dbgSBase, "Supervisor Address: 0x%.16llx\n", device.Supervisor); + fprintf(dbgSBase, "Device type index: %d\n", device.DeviceType); + fprintf(dbgSBase, "Attributes: %d\n", device.Attribute); + fprintf(dbgSBase, "Record type: %d\n", static_cast(device.RecordType)); + fprintf(dbgSBase, "\n"); + unsigned err = AddressBook::SUCCESS; + if ((err = AddDevice(taskName, device)) != AddressBook::SUCCESS) + { + /* + if (ClearTask(taskName) != AddressBook::SUCCESS) + { + Post(708, device.Name, taskName, int2str(Urank)); + return err; + } + */ + Post(707, device.Name, taskName, int2str(Urank)); + // return err; + } + } + fclose(dbgSBase); + return AddressBook::SUCCESS; +} + +// ----------------------------------------------------------------------------- + +unsigned SBase::DumpAll(PMsg_p *msg) +{ + // filename must be treated as an absolute literal, because tasks could in + // general have different paths, therefore there is no way to infer what + // additional path string might be appended to the file name give. + string filename = msg->Zname(1); + FILE* dumpFile; + if (filename.empty()) dumpFile = stdout; + else dumpFile = fopen(filename.c_str(), "a"); + vector tasks; + unsigned err = AddressBook::SUCCESS; + if (err = ListTask(tasks)) + { + Post(709, int2str(Urank)); + if (dumpFile != stdout) fclose(dumpFile); + return err; + } + WALKVECTOR(string, tasks, task) Dump(dumpFile, *task); + return err; +} + +unsigned SBase::DumpSummary(PMsg_p *msg) +{ + string filename = msg->Zname(1); + FILE* dumpFile; + if (filename.empty()) dumpFile = stdout; + else dumpFile = fopen(filename.c_str(), "a"); + Dump(dumpFile); + if (dumpFile != stdout) fclose(dumpFile); + return AddressBook::SUCCESS; +} + +unsigned SBase::DumpTask(PMsg_p *msg) +{ + string taskName = msg->Zname(0); + if (taskName.empty()) + { + Post(710, int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + string filename = msg->Zname(1); + FILE* dumpFile; + if (filename.empty()) dumpFile = stdout; + else dumpFile = fopen(filename.c_str(), "a"); + Dump(dumpFile, taskName); + if (dumpFile != stdout) fclose(dumpFile); + return AddressBook::SUCCESS; +} + +unsigned SBase::QueryAttr(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::QueryDevIAll(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::QueryDevIByName(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::QueryDevIByID(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::QueryDevT(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::QueryExtn(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::QueryList(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::QuerySupv(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::QueryTask(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::SendAttr(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::SendDevIAll(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::SendDevIByName(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::SendDevIByID(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::SendDevT(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::SendExtn(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned SBase::SendSupv(PMsg_p *msg, unsigned comm) +{ + return 0; +} diff --git a/Source/NameServer/SBase.h b/Source/NameServer/SBase.h new file mode 100644 index 00000000..7cc82b21 --- /dev/null +++ b/Source/NameServer/SBase.h @@ -0,0 +1,76 @@ +#ifndef __SBASE_H__ +#define __SBASE_H__ + +#include "CommonBase.h" +#include "AddressBook.hpp" +using namespace AddressBookNS; + +//============================================================================== + +class SBase : public CommonBase, public AddressBook +{ + +public: + SBase(int,char **,string,string); +virtual ~ SBase(); + +inline void Dump(FILE * f = stdout, string s = "") {AddressBook::Dump(f, s);}; + +unsigned OnCfg (PMsg_p *, unsigned); +unsigned OnCmd (PMsg_p *, unsigned); +unsigned OnData (PMsg_p *, unsigned); +unsigned OnDump (PMsg_p *, unsigned); +unsigned OnQuery (PMsg_p *, unsigned); +unsigned OnReply (PMsg_p *, unsigned); +unsigned OnSend (PMsg_p *, unsigned); + +protected: + +virtual unsigned Connect (string=""); + +virtual unsigned ConfigBuild (PMsg_p *, unsigned); +virtual unsigned ConfigDelete (PMsg_p *, unsigned); +virtual unsigned ConfigDir (PMsg_p *, unsigned); +virtual unsigned ConfigDistribute (PMsg_p *, unsigned); +virtual unsigned ConfigRecall (PMsg_p *, unsigned); +virtual unsigned ConfigState (PMsg_p *, unsigned); +virtual unsigned QueryDevIAll (PMsg_p *, unsigned); +virtual unsigned QueryDevIByName (PMsg_p *, unsigned); +virtual unsigned QueryDevIByID (PMsg_p *, unsigned); +virtual unsigned QuerySupv (PMsg_p *, unsigned); +virtual unsigned SendAttr (PMsg_p *, unsigned); +virtual unsigned SendDevIAll (PMsg_p *, unsigned); +virtual unsigned SendDevIByName (PMsg_p *, unsigned); +virtual unsigned SendDevIByID (PMsg_p *, unsigned); +virtual unsigned SendDevT (PMsg_p *, unsigned); +virtual unsigned SendExtn (PMsg_p *, unsigned); +virtual unsigned SendSupv (PMsg_p *, unsigned); + +typedef unsigned (SBase::*pMeth)(PMsg_p*, unsigned); +typedef map FnMap_t; +vector FnMapx; + +private: + +unsigned CmdIntegrity (PMsg_p *); +unsigned DataTask (PMsg_p *); +unsigned DataDevType (PMsg_p *); +unsigned DataDevice (PMsg_p *); +unsigned DataDeviceExternal (PMsg_p *); +unsigned DataExternal (PMsg_p *); +unsigned DataSupervisor (PMsg_p *); +unsigned DumpAll (PMsg_p *); +unsigned DumpSummary (PMsg_p *); +unsigned DumpTask (PMsg_p *); +unsigned QueryAttr (PMsg_p *, unsigned); +unsigned QueryDevT (PMsg_p *, unsigned); +unsigned QueryExtn (PMsg_p *, unsigned); +unsigned QueryList (PMsg_p *, unsigned); +unsigned QueryTask (PMsg_p *, unsigned); + +}; + +//============================================================================== + +#endif + diff --git a/Source/OrchBase/OrchBase.cpp b/Source/OrchBase/OrchBase.cpp index cff6faca..0958f38d 100644 --- a/Source/OrchBase/OrchBase.cpp +++ b/Source/OrchBase/OrchBase.cpp @@ -16,6 +16,11 @@ #include "MultiSimpleDeployer.h" #include "Ns_el.h" #include "P_super.h" +#include "poets_msg.h" // brings in constants to set hardware addresses. This should be rationalised. +#include "ABDefs.hpp" // TODO: integrate 11 July 2019 ADR +#include "ABRecord.hpp" +#include // debug +using namespace AddressBookNS; //============================================================================== // This class is chopped up into multiple .cpp files, because it's so diff --git a/Source/OrchBase/OrchBaseLink.cpp b/Source/OrchBase/OrchBaseLink.cpp index 26334cbb..9151aa04 100644 --- a/Source/OrchBase/OrchBaseLink.cpp +++ b/Source/OrchBase/OrchBaseLink.cpp @@ -144,7 +144,176 @@ if (pT->linked) { // Task already linked? */ // Place and set the link flag if the placement was successful. -if (!pPlace->Place(pT)) pT->LinkFlag(); +// if (!pPlace->Place(pT)) pT->LinkFlag(); +if (!pPlace->Place(pT)) +{ + // send data to NameServer. Once the P_builder is threaded, this + // should be moved to the build command. + int nsRank = Q::NAP; + MPI_Comm nsComm = MPI_COMM_NULL; + vector >superRanks; + for (unsigned comm = 0; comm < Comms.size(); comm++) + { + WALKVECTOR(int, pPmap[comm]->U.Mothership, m) + superRanks.push_back(pair(comm, *m)); + if ((nsRank = pPmap[comm]->U.NameServer) != Q::NAP) + { + nsComm = Comms[comm]; + break; + } + } + if (nsRank == Q::NAP) + { + Post(711); // No nameserver. This should be fatal + return; // (need to return with a value from TaskLoad) + } + if (nsComm == MPI_COMM_NULL) + { + Post(712); // Unknown comm for nameserver. VERY fatal + return; // (should abort) + } + PMsg_p deviceData(nsComm); + vector supervisorNames; + vector supervisorAddrs; + vector supervisorRanks; + string supervisorType = pT->pSup->pP_devtyp->Name(); + vector supervisorAttrs; + // vector supervisorDevices; + vector >::iterator currSuperRank = superRanks.begin(); + deviceData.Src(Urank); + deviceData.Tgt(nsRank); + deviceData.Zname(0,pT->Name()); + WALKMAP(AddressComponent,P_box*,pE->P_boxm,box) + { + // find and set up supervisor devices + vector::iterator superIter = box->second->P_superv.begin(); + while (((*superIter) != pT->pSup) && (++superIter != box->second->P_superv.end())); + if (superIter == box->second->P_superv.end()) + { + Post(164,pT->Name()); + return; + } + if (!((*superIter)->pP_devtyp)) + { + Post(165,(*superIter)->Name()); + return; + } + // RecordData_t supervisorRecord; + P_addr sAddr = (*superIter)->addr; + supervisorNames.push_back((*superIter)->Name()); + // temporarily addresses must use old format. Later this should + // be changed to the definitive reference standard format. + //supervisorRecord.Address = (sAddr.A_box << P_BOX_OS) | + // (sAddr.A_board << P_BOARD_OS) | + // (sAddr.A_core << P_CORE_OS) | + // (sAddr.A_thread << P_THREAD_OS) | + // (sAddr.A_device << P_DEVICE_OS); + supervisorAddrs.push_back((sAddr.A_box << P_BOX_OS) | + (sAddr.A_board << P_BOARD_OS) | + (sAddr.A_core << P_CORE_OS) | + (sAddr.A_thread << P_THREAD_OS) | + (sAddr.A_device << P_DEVICE_OS)); + //supervisorRecord.DeviceType = sDevType; + //supervisorRecord.RecordType = Supervisor; + supervisorAttrs.push_back(0); + // Temporarily, for testing without Motherships, we will give the Supervisor + // a 'bad' rank. When running with a system that includes Motherships the + // commented code below should be selected. + if (currSuperRank == superRanks.end()) + supervisorRanks.push_back(0xFFFFFFFF); + // supervisorRecord.Rank = 0xFFFFFFFF; // when record bundled as a RecordData_t + /* { + Post(709,(*superIter)->Name(),box->first); + } + else + { + supervisorRecord.Rank = currSuperRank->second; + ++currSuperRank; + supervisorDevices.push_back(supervisorRecord); + } + */ + else + { + // naïvely, we will assume that the MPI ranks are in box-address order. + // later, this should be fixed. + // TODO: extend the rank in a Record_t to include the comm. + supervisorRanks.push_back(currSuperRank->second); + // supervisorRecord.Rank = currSuperRank->second; + ++currSuperRank; + } + // supervisorDevices.push_back(supervisorRecord); + // and then find all the 'normal' devices + WALKVECTOR(P_board*,box->second->P_boardv,board) + { + + for (map::node>::iterator mailbox = (*board)->G.index_n.begin();mailbox != (*board)->G.index_n.end();mailbox++) + { + WALKMAP(unsigned,P_core*,mailbox->second.data->P_corem,core) + { + WALKMAP(unsigned,P_thread*,core->second->P_threadm,thread) + { + if (thread->second->P_devicel.size() && (thread->second->P_devicel.front()->par->par == pT)) + { + vector devNames; + vector devAddrs; + SymAddr_t devSuper; + // any given message will only contain one device type, corresponding + // to the one used in a particular thread. + string devTyp = thread->second->P_devicel.front()->pP_devtyp->Name(); + vector devAttrs; + // vector devices; + // RecordData_t deviceRecord; + DTypeIdx devType = 0; + // deviceRecord.Supervisor = supervisorRecord.Address; + // deviceRecord.RecordType = Device; + // deviceRecord.DeviceType = devType; + WALKLIST(P_device*,thread->second->P_devicel,device) + { + P_addr dAddr = (*device)->addr; + // temporarily addresses must use old format. Later this should + // be changed to the definitive reference standard format. + // deviceRecord.Address = (dAddr.A_box << P_BOX_OS) | + // (dAddr.A_board << P_BOARD_OS) | + // (dAddr.A_core << P_CORE_OS) | + // (dAddr.A_thread << P_THREAD_OS) | + // (dAddr.A_device << P_DEVICE_OS); + devAddrs.push_back((dAddr.A_box << P_BOX_OS) | + (dAddr.A_board << P_BOARD_OS) | + (dAddr.A_core << P_CORE_OS) | + (dAddr.A_thread << P_THREAD_OS) | + (dAddr.A_device << P_DEVICE_OS)); + devNames.push_back((*device)->Name()); + // devices.push_back(deviceRecord); + devAttrs.push_back(0); + } + deviceData.Key(Q::NAME,Q::DATA,Q::DEVI); + deviceData.Zname(2,devTyp); + deviceData.PutX(0,&devNames); + // deviceData.Put(1,&devices); + deviceData.Put(1,&devAddrs); + deviceData.Put(2,&devAttrs); + deviceData.Put(3,&supervisorAddrs.back()); + // ___________________________________________________________________ + // Post(713,int2str(deviceData.Tgt()),int2str(deviceData.Src()),thread->second->Name()); + deviceData.Send(); + } + } + } + } + } + } + deviceData.Key(Q::NAME,Q::DATA,Q::SUPV); + deviceData.Zname(2,supervisorType); + deviceData.PutX(0,&supervisorNames); + // deviceData.Put(1,&supervisorDevices); + deviceData.Put(1,&supervisorAddrs); + deviceData.Put(2,&supervisorAttrs); + deviceData.Put(3,&supervisorRanks); + // Post(714,int2str(deviceData.Tgt()),int2str(deviceData.Src()),pT->Name()); + deviceData.Send(); + pT->LinkFlag(); + pT->state = Linked; +} /* diff --git a/Source/OrchBase/OrchBaseTask.cpp b/Source/OrchBase/OrchBaseTask.cpp index 7c6ebdaf..2b7f9fcf 100644 --- a/Source/OrchBase/OrchBaseTask.cpp +++ b/Source/OrchBase/OrchBaseTask.cpp @@ -247,12 +247,124 @@ if (!ufs.empty()) { if (tfp!=0) fp = tfp; else Post(109,ufs); } +// added additional dump parameters: summary (-s), NameServer (-n), +// task name ( || "*") 13 July 2019 ADR +string ufo = Cl.GetO(1); // Look for summary operator "-" +string ufn = Cl.GetP(1); +if (!(ufo.empty() || ufo == "-")) // reject other operators +{ + Post(25, ufo+ufs, "task /dump"); + return; +} +bool summary = false; +bool dumpNs = false; +if (!ufn.empty()) // look for dump qualifiers +{ + if (ufo == "-") // expect a summary with a "-" sign + { + if (ufn != "s") + { + Post(25, ufo+ufn, "task /dump"); // other switches are invalid + return; + } + summary = true; + ufn = "*"; // summary always outputs all tasks + } + ufo = Cl.GetO(2); + if (!(ufo.empty() || ufo == "-")) // reject other operators + { + Post(25, ufo+Cl.GetP(2), "task /dump"); + return; + } + if (ufo == "-") // expect a NameServer dump with a "-" sign + { + if (Cl.GetP(2) != "n") // look for the "-n" + { + Post(25, ufo+Cl.GetP(2), "task /dump"); // other switches are invalid + return; + } + dumpNs = true; + } + +} +else ufn = "*"; // default to dumping all tasks. +map::iterator task = P_taskm.begin(); // may need to find a specific task + // Just the task structures if (P_taskm.empty()) fprintf(fp,"Task map empty\n"); -else WALKMAP(string,P_task *,P_taskm,i) (*i).second->Dump(fp); -if (P_typdclm.empty()) fprintf(fp,"Type declaration map empty\n"); -else WALKMAP(string,P_typdcl *,P_typdclm,i) (*i).second->Dump(fp); -if (fp!=stdout) fclose(fp); +else +{ + if (ufn == "*") // all tasks + WALKMAP(string,P_task *,P_taskm,i) i->second->Dump(fp); + else // a named task given by parameter 1 + { + task = P_taskm.find(ufn); + if (task == P_taskm.end()) + { + Post(105,ufn); + fprintf(fp,"No such task: %s\n", ufn.c_str()); + } + else task->second->Dump(fp); + } +} +if (!summary) // print out type declares if not simply doing a summary +{ + if (P_typdclm.empty()) fprintf(fp,"Type declaration map empty\n"); + else + { + if (ufn == "*") // all type declarations + WALKMAP(string,P_typdcl *,P_typdclm,i) (*i).second->Dump(fp); + else if (task != P_taskm.end()) // just the type declaration for a named task + { + if (!task->second->pP_typdcl) // if it exists! + { + Post(113,ufn); + fprintf(fp,"Task %s has no type declarations\n", ufn.c_str()); + } + else task->second->pP_typdcl->Dump(fp); + } + } +} +// close the file BEFORE sending to NameServer, so that if it happens to be on the +// same machine, the dumpfile won't have multiple open handles. +if (fp!=stdout) fclose(fp); +if (dumpNs) // also dump NameServer info? +{ + int nsRank = Q::NAP; // search for NameServer + MPI_Comm nsComm = MPI_COMM_NULL; + for (unsigned comm = 0; comm < Comms.size(); comm++) + { + // find NameServer's comm + if ((nsRank = pPmap[comm]->U.NameServer) != Q::NAP) + { + nsComm = Comms[comm]; + break; + } + } + if (nsRank == Q::NAP) + { + Post(711); // No nameserver. This should be fatal + return; // (need to return with a value from TaskDump) + } + if (nsComm == MPI_COMM_NULL) + { + Post(712); // Unknown comm for nameserver. VERY fatal + return; // (should abort) + } + PMsg_p nsDump(nsComm); // set up a dump message + nsDump.Src(Urank); + nsDump.Tgt(nsRank); + // of the appropriate type (summary, all, or a named task) + if (summary) nsDump.Key(Q::NAME,Q::DUMP,Q::LIST); + if (ufn == "*") nsDump.Key(Q::NAME,Q::DUMP,Q::TASK,Q::ALL); + else + { + nsDump.Key(Q::NAME,Q::DUMP,Q::TASK,Q::NM); + nsDump.Zname(0,ufn); + } + nsDump.Zname(1,ufs); // NameServer should open the appropriate dumpfile + nsDump.Send(); +} } //------------------------------------------------------------------------------ @@ -289,7 +401,7 @@ if (Cl.Pa_v.size()!=1) { // Command make sense? Post(47,Cl.Cl,"task","1"); return; } -string file = Cl.Pa_v[0].Val; // Unpack file name +string file = Cl.GetP(0); // Unpack file name file = taskpath + file; // Add default full file path FileName Fn(file); // Make sure it's a semantic filename if (Fn.Err()) Post(104,file); // If not, warn the monkey @@ -312,6 +424,103 @@ if (Fn.Err()) Post(104,file); // If not, warn the monkey // pB->Build(pT); // Build the thing pB->Load(file); // Parse the xml description of the task //pB->Build(); // Build the thing + +// additions for NameServer/SBase 11 July 2019 ADR +int nsRank = Q::NAP; +MPI_Comm nsComm = MPI_COMM_NULL; +for (unsigned comm = 0; comm < Comms.size(); comm++) +{ + if ((nsRank = pPmap[comm]->U.NameServer) != Q::NAP) + { + nsComm = Comms[comm]; + break; + } +} +if (nsRank == Q::NAP) +{ + Post(711); // No nameserver. This should be fatal + return; // (need to return with a value from TaskLoad) +} +if (nsComm == MPI_COMM_NULL) +{ + Post(712); // Unknown comm for nameserver. VERY fatal + return; // (should abort) +} +PMsg_p nsTask(nsComm); +nsTask.Src(Urank); +WALKMAP(string, P_task*, P_taskm, task) +{ + if (task->second->state == Unknown) + { + nsTask.Key(Q::NAME,Q::DATA,Q::TASK); + nsTask.Zname(0,task->first); + nsTask.Zname(1,taskpath); + nsTask.Zname(2,file); + vector msgs; + WALKVECTOR(P_message*,task->second->pP_typdcl->P_messagev,msg) msgs.push_back((*msg)->Name()); + nsTask.PutX(0, &msgs); + // in future should place attributes here. + vector attrs; + attrs.push_back("_NO_ATTRIBUTE_"); + nsTask.PutX(1,&attrs); + vector counts; + unsigned long deviceCount = 0; + // tediously, we have to walk the graph because supervisors and externals] + // must be disambiguated. + for (map::node>::iterator dn = task->second->pD->G.index_n.begin(); dn != task->second->pD->G.index_n.end(); dn++) + if (dn->second.data != task->second->pSup) ++deviceCount; + counts.push_back(deviceCount); + // it would be so much easier if we could do something like the following! + // counts.push_back(task->second->pD->G.SizeNodes()); + counts.push_back(0); // number of externals. In future this will need to be distinguished. + deviceCount = 0; + set taskBoxes; + WALKVECTOR(P_board*, task->second->pSup->P_boardv, board) + { + if (taskBoxes.find((*board)->parent) == taskBoxes.end()) + { + taskBoxes.insert((*board)->parent); + ++deviceCount; + } + } + counts.push_back(deviceCount); + nsTask.Put(2,&counts); + nsTask.Send(nsRank); + nsTask.Clear(); + nsTask.Src(Urank); + nsTask.Key(Q::NAME,Q::DATA,Q::DEVT); + nsTask.Zname(0,task->first); + WALKVECTOR(P_devtyp*,task->second->pP_typdcl->P_devtypv,devType) + { + nsTask.Zname(2,(*devType)->Name()); + vectorinMsgs; + vectoroutMsgs; + for (unsigned m = 0; m < task->second->pP_typdcl->P_messagev.size(); m++) + { + WALKVECTOR(P_pintyp*,(*devType)->P_pintypIv,iPin) + { + if ((*iPin)->pMsg == task->second->pP_typdcl->P_messagev[m]) + { + inMsgs.push_back(m); + break; + } + } + WALKVECTOR(P_pintyp*,(*devType)->P_pintypOv,oPin) + { + if ((*oPin)->pMsg == task->second->pP_typdcl->P_messagev[m]) + { + outMsgs.push_back(m); + break; + } + } + } + nsTask.Put(0,&inMsgs); + nsTask.Put(1,&outMsgs); + nsTask.Send(nsRank); + } + task->second->state = Loaded; + } +} } //------------------------------------------------------------------------------ diff --git a/Source/OrchBase/P_super.cpp b/Source/OrchBase/P_super.cpp index 691dc479..6ce9384e 100644 --- a/Source/OrchBase/P_super.cpp +++ b/Source/OrchBase/P_super.cpp @@ -74,6 +74,11 @@ void P_super::Attach(P_board* targetBoard) // Store the index in the board, so the board can access the supervisor by // querying its parent box. targetBoard->sup_offv.push_back(supervisorIndex); + // append the box address to the supervisor if it's not already there. + // added 12 July 2019 ADR + unsigned boxAddr; + if (!addr.GetBox(boxAddr)) + addr.SetBox(parentBox->get_hardware_address()->get_box()); } //------------------------------------------------------------------------------ diff --git a/Source/OrchBase/P_task.cpp b/Source/OrchBase/P_task.cpp index 72d3a73e..1851efa1 100644 --- a/Source/OrchBase/P_task.cpp +++ b/Source/OrchBase/P_task.cpp @@ -10,6 +10,7 @@ P_task::P_task(OrchBase * _p,string _s):par(_p),pSup(0),pD(0),pP_typdcl(0) Name(_s); // Save name Npar(_p); // Namebase parent linked = false; // Not linked yet +state = AddressBookNS::Unknown; // Don't know state yet (ADR 11 July 2019) pOwn = 0; // No owner yet pD = new D_graph(this,"D_graph"); } diff --git a/Source/OrchBase/P_task.h b/Source/OrchBase/P_task.h index 9fb7fbc3..b8d89023 100644 --- a/Source/OrchBase/P_task.h +++ b/Source/OrchBase/P_task.h @@ -4,6 +4,7 @@ #include #include "NameBase.h" #include "OrchBase.h" +#include "ABDefs.hpp" // TODO: take TaskState_t and refactor into a common .h class P_owner; //#include "P_owner.h" @@ -27,6 +28,7 @@ P_typdcl * pP_typdcl; // Declare block string filename; // XML source from HbD-land P_owner * pOwn; // Task owner bool linked; +AddressBookNS::TaskState_t state; // Task state (TODO: integrate this) struct PoL_t { public: diff --git a/Source/OrchBase/build_defs.cpp b/Source/OrchBase/build_defs.cpp index 2c5f7c48..ba5cc043 100644 --- a/Source/OrchBase/build_defs.cpp +++ b/Source/OrchBase/build_defs.cpp @@ -21,7 +21,7 @@ const unsigned int MAX_DEVICES_PER_THREAD = 1024; const unsigned int LOG_DEVICES_PER_THREAD = 10; const unsigned int THREADS_PER_CORE = 0x1 << TinselLogThreadsPerCore; const unsigned int CORES_PER_BOARD = 0x1 << TinselLogCoresPerBoard; -const unsigned int BOARDS_PER_BOX = TinselMeshXLen*TinselMeshYLen; +const unsigned int BOARDS_PER_BOX = TinselMeshXLenWithinBox*TinselMeshYLenWithinBox; const unsigned long long MEM_PER_BOARD = ((long long) 0x1 << TinselLogBytesPerDRAM) * TinselDRAMsPerBoard; const unsigned int PIN_POS = 24; const bool SHARED_INSTR_MEM = TinselSharedInstrMem; diff --git a/Source/OrchBase/build_defs.h b/Source/OrchBase/build_defs.h index 2416ced4..0e94b18e 100644 --- a/Source/OrchBase/build_defs.h +++ b/Source/OrchBase/build_defs.h @@ -2,7 +2,8 @@ #define __BUILD_DEFS_H__ #include -#include "tinsel-config.h" +#include "config.h" +//#include "tinsel-config.h" extern const std::string SYS_COPY; extern const std::string RECURSIVE_CPY; diff --git a/Source/OrchestratorMessages.txt b/Source/OrchestratorMessages.txt index cc80df89..1bcc3aef 100644 --- a/Source/OrchestratorMessages.txt +++ b/Source/OrchestratorMessages.txt @@ -72,6 +72,9 @@ 161(W) : "Owner %s does not exist" 162(E) : "No NameServer is running - cannot upload task" 163(E) : "Task %s too large for system - cannot place" +164(E) : "No supervisor found for task %s - cannot map" +165(E) : "Device %s has no or an invalid device type" +166(E) : "Supervisor device %s has no or an invalid device type" 508(U) : "Task %s in an unrecognised state. Reboot the mothership" 509(W) : "Task %s could not be loaded because task %s was already active in state %s" @@ -95,6 +98,28 @@ 601(I) : "Core %s: (part %s): %s" 607(W) : "Attempted to recall a task %s that was not deployed to the Mothership" +700(W) : "Unrecognised command %s to SBase process rank %s" +701(U) : "Bad command switch in SBase process rank %s" +702(W) : "Unrecognised query subtype %s to SBase process rank %s" +703(E) : "Unexpected reply to query: %s received at SBase process rank %s. Circular routing loop?" +704(E) : "Tried to load task into SBase at rank %s with no name" +705(E) : "Error while loading SBase data at rank %s: no task name" +706(E) : "Error loading device type data for task %s into SBase at rank %s: no device type name" +707(E) : "Error loading device %s for task %s into SBase at rank %s" +708(E) : "Error loading device %s for task %s into SBase at rank %s: no such task" +709(W) : "Unable to register supervisor %s on box %s into SBase: not found in the rank list. May need to connect" +710(W) : "Empty task for dump request on SBase at rank %s" +711(S) : "Error: no nameserver found" +712(U) : "Fatal error: nameserver on nonexistent comm" +713(I) : "Sending information to nameserver rank %s from root rank %s on devices on thread %s" +714(I) : "Sending information to nameserver rank %s from root rank %s on supervisors for task %s" +715(I) : "SBase rank %s received information from rank %s on devices for task %s" +716(E) : "Mismatch in device name-data info to SBase at rank %s: %s device names, %s %ss received" +717(E) : "Error sending device data to SBase at rank %s for device %s: invalid record type %s" +718(E) : "Error: no supervisor found for device records for task %s on SBase at rank %s" +719(E) : "Error loading device records for task %s on SBase at rank %s: no such device type %s" +720(W) : "Mismatch in number of device counts sent: %s, expected %s for task %s to SBase at rank %s" + 801(D) : "P_builder::Add(name=%s,file=%s)" 802(D) : "Removing task %s from the Orchestrator" 803(F) : "P_builder:out of memory to create a QCoreApplication object" From de6273f1f19c5af8e709e8b2517445ebf5cc2c69 Mon Sep 17 00:00:00 2001 From: AlexRast Date: Fri, 19 Jul 2019 17:29:32 +0100 Subject: [PATCH 02/14] removed debug file and added a few fixes for state update --- Source/NameServer/AddressBook.cpp | 3 - Source/NameServer/SBase.cpp | 122 ++++++++++++++++++++++++++---- Source/OrchBase/OrchBaseTask.cpp | 12 +-- Source/OrchestratorMessages.txt | 13 ++-- 4 files changed, 115 insertions(+), 35 deletions(-) diff --git a/Source/NameServer/AddressBook.cpp b/Source/NameServer/AddressBook.cpp index a78e8af6..45c00759 100644 --- a/Source/NameServer/AddressBook.cpp +++ b/Source/NameServer/AddressBook.cpp @@ -175,12 +175,9 @@ unsigned AddressBook::ClearTask(const std::string &TaskName) TaskData_t cTask; unsigned err = SUCCESS; if ((err = GetTask(TaskName, cTask)) != SUCCESS) return err; - cTask.DeviceCount = 0; cTask.DeviceCountLd = 0; - cTask.ExternalCount = 0; cTask.ExternalCountLd = 0; cTask.SupervisorCount = 0; - cTask.DeviceTypes.clear(); if ((err = DelTask(TaskName)) != SUCCESS) return err; return AddTask(TaskName, cTask); } diff --git a/Source/NameServer/SBase.cpp b/Source/NameServer/SBase.cpp index 5cf9b8d6..050086b1 100644 --- a/Source/NameServer/SBase.cpp +++ b/Source/NameServer/SBase.cpp @@ -306,12 +306,59 @@ unsigned SBase::CmdIntegrity(PMsg_p *msg) unsigned SBase::ConfigBuild(PMsg_p *msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + vector tasks; + unsigned err = SUCCESS; + if (taskName.empty()) + { + if (err = ListTask(tasks)) + { + Post(721,"rebuild","list",int2str(Urank)); + return err; + } + } + else tasks.push_back(taskName); + vector::iterator task; + for (task = tasks.begin(); task != tasks.end(); task++) + { + if (!(err = RebuildTask(*task))) + { + if (!(err = BuildMaps(*task))) + err = BuildLink(*task); + } + if (err) + { + Post(721,"rebuild",*task,int2str(Urank)); + return err; + } + } + return err; } unsigned SBase::ConfigDelete(PMsg_p *msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + vector tasks; + unsigned err = SUCCESS; + if (taskName.empty()) + { + if (err = ListTask(tasks)) + { + Post(721,"gett","list",int2str(Urank)); + return err; + } + } + else tasks.push_back(taskName); + vector::iterator task; + for (task = tasks.begin(); task != tasks.end(); task++) + { + if (err = DelTask(*task)) + { + Post(721,"delet",*task,int2str(Urank)); + return err; + } + } + return err; } unsigned SBase::ConfigDistribute(PMsg_p *msg, unsigned comm) @@ -321,12 +368,46 @@ unsigned SBase::ConfigDistribute(PMsg_p *msg, unsigned comm) unsigned SBase::ConfigDir(PMsg_p *msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + string taskPath = msg->Zname(1); + if (taskPath.empty()) Post(723,int2str(Urank)); + if (TaskExecPath(taskName,taskPath)) + { + Post(724,taskName,int2str(Urank)); + return ERR_TASK_NOT_FOUND; + } + return SUCCESS; } unsigned SBase::ConfigRecall(PMsg_p *msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + vector tasks; + unsigned err = SUCCESS; + if (taskName.empty()) + { + if (err = ListTask(tasks)) + { + Post(721,"gett","list",int2str(Urank)); + return err; + } + } + else tasks.push_back(taskName); + vector::iterator task; + for (task = tasks.begin(); task != tasks.end(); task++) + { + if (err = ClearTask(*task)) + { + Post(721,"clear",*task,int2str(Urank)); + return err; + } + if (err = TaskState(*task,Unknown)) + { + Post(725,*task,int2str(Urank)); + return err; + } + } + return err; } unsigned SBase::ConfigState(PMsg_p *msg, unsigned comm) @@ -349,12 +430,11 @@ unsigned SBase::DataTask(PMsg_p *msg) msg->GetX(1, task.AttributeTypes); vector counts; msg->Get(2, counts); - if (counts.size() != 3) Post(720,uint2str(counts.size()),"3",task.Name,int2str(Urank)); + if (counts.size() != 2) Post(720,uint2str(counts.size()),"2",task.Name,int2str(Urank)); else { task.DeviceCount = counts[0]; task.ExternalCount = counts[1]; - task.SupervisorCount = counts[2]; } return AddTask(task.Name, task); } @@ -402,7 +482,7 @@ unsigned SBase::DataDevice(PMsg_p *msg) Post(718,taskName,int2str(Urank),devTypName); return AddressBook::ERR_INVALID_DEVTYPE; } - FILE* dbgSBase = fopen("Root2NS_SBase_devices.txt","a"); // DEBUG ONLY + // FILE* dbgSBase = fopen("Root2NS_SBase_devices.txt","a"); // DEBUG ONLY vector devNames; // vector devData; vector devAddrs; @@ -434,10 +514,13 @@ unsigned SBase::DataDevice(PMsg_p *msg) // Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devData.size())); // return ERR_DEVICE_DATA_MISMATCH; // } + /* fprintf(dbgSBase, "Nameserver received device info for %u devices\n", devNames.size()); fprintf(dbgSBase, "\n"); fprintf(dbgSBase, "________________________________________________________________________________\n"); fprintf(dbgSBase, "\n"); + */ + unsigned err = AddressBook::SUCCESS; for (unsigned d = 0; d < devNames.size(); d++) { Record_t device(devNames[d], @@ -452,6 +535,7 @@ unsigned SBase::DataDevice(PMsg_p *msg) // devData[d].DeviceType, // devData[d].RecordType, // devData[d].Attribute); + /* fprintf(dbgSBase, "Device %s:\n", device.Name.c_str()); fprintf(dbgSBase, "Address: 0x%.16llx\n", device.Address); if (device.RecordType == Supervisor) @@ -461,21 +545,27 @@ unsigned SBase::DataDevice(PMsg_p *msg) fprintf(dbgSBase, "Attributes: %d\n", device.Attribute); fprintf(dbgSBase, "Record type: %d\n", static_cast(device.RecordType)); fprintf(dbgSBase, "\n"); - unsigned err = AddressBook::SUCCESS; + */ if ((err = AddDevice(taskName, device)) != AddressBook::SUCCESS) { - /* if (ClearTask(taskName) != AddressBook::SUCCESS) { Post(708, device.Name, taskName, int2str(Urank)); return err; } - */ Post(707, device.Name, taskName, int2str(Urank)); - // return err; + return err; } } - fclose(dbgSBase); + if (TaskState(taskName) < Linked) + { + if (err = TaskState(taskName,Linked)) + { + Post(724,taskName,int2str(Urank)); + return err; + } + } + // fclose(dbgSBase); return AddressBook::SUCCESS; } @@ -518,7 +608,7 @@ unsigned SBase::DataSupervisor(PMsg_p *msg) Post(718,taskName,int2str(Urank),devTypName); return AddressBook::ERR_INVALID_DEVTYPE; } - FILE* dbgSBase = fopen("Root2NS_SBase_devices.txt","a"); // DEBUG ONLY + // FILE* dbgSBase = fopen("Root2NS_SBase_devices.txt","a"); // DEBUG ONLY vector devNames; // vector devData; vector devAddrs; @@ -549,10 +639,12 @@ unsigned SBase::DataSupervisor(PMsg_p *msg) // Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devData.size())); // return ERR_DEVICE_DATA_MISMATCH; // } + /* fprintf(dbgSBase, "Nameserver received supervisor info for %u supervisors\n", devNames.size()); fprintf(dbgSBase, "\n"); fprintf(dbgSBase, "________________________________________________________________________________\n"); fprintf(dbgSBase, "\n"); + */ for (unsigned d = 0; d < devNames.size(); d++) { Record_t device(devNames[d], @@ -567,6 +659,7 @@ unsigned SBase::DataSupervisor(PMsg_p *msg) // devData[d].DeviceType, // devData[d].RecordType, // devData[d].Attribute); + /* fprintf(dbgSBase, "Device %s:\n", device.Name.c_str()); fprintf(dbgSBase, "Address: 0x%.16llx\n", device.Address); if (device.RecordType == Supervisor) @@ -576,6 +669,7 @@ unsigned SBase::DataSupervisor(PMsg_p *msg) fprintf(dbgSBase, "Attributes: %d\n", device.Attribute); fprintf(dbgSBase, "Record type: %d\n", static_cast(device.RecordType)); fprintf(dbgSBase, "\n"); + */ unsigned err = AddressBook::SUCCESS; if ((err = AddDevice(taskName, device)) != AddressBook::SUCCESS) { @@ -590,7 +684,7 @@ unsigned SBase::DataSupervisor(PMsg_p *msg) // return err; } } - fclose(dbgSBase); + // fclose(dbgSBase); return AddressBook::SUCCESS; } diff --git a/Source/OrchBase/OrchBaseTask.cpp b/Source/OrchBase/OrchBaseTask.cpp index 2b7f9fcf..49ff9c2e 100644 --- a/Source/OrchBase/OrchBaseTask.cpp +++ b/Source/OrchBase/OrchBaseTask.cpp @@ -465,7 +465,7 @@ WALKMAP(string, P_task*, P_taskm, task) nsTask.PutX(1,&attrs); vector counts; unsigned long deviceCount = 0; - // tediously, we have to walk the graph because supervisors and externals] + // tediously, we have to walk the graph because supervisors and externals // must be disambiguated. for (map::node>::iterator dn = task->second->pD->G.index_n.begin(); dn != task->second->pD->G.index_n.end(); dn++) if (dn->second.data != task->second->pSup) ++deviceCount; @@ -474,16 +474,6 @@ WALKMAP(string, P_task*, P_taskm, task) // counts.push_back(task->second->pD->G.SizeNodes()); counts.push_back(0); // number of externals. In future this will need to be distinguished. deviceCount = 0; - set taskBoxes; - WALKVECTOR(P_board*, task->second->pSup->P_boardv, board) - { - if (taskBoxes.find((*board)->parent) == taskBoxes.end()) - { - taskBoxes.insert((*board)->parent); - ++deviceCount; - } - } - counts.push_back(deviceCount); nsTask.Put(2,&counts); nsTask.Send(nsRank); nsTask.Clear(); diff --git a/Source/OrchestratorMessages.txt b/Source/OrchestratorMessages.txt index 1bcc3aef..93c64303 100644 --- a/Source/OrchestratorMessages.txt +++ b/Source/OrchestratorMessages.txt @@ -119,14 +119,18 @@ 718(E) : "Error: no supervisor found for device records for task %s on SBase at rank %s" 719(E) : "Error loading device records for task %s on SBase at rank %s: no such device type %s" 720(W) : "Mismatch in number of device counts sent: %s, expected %s for task %s to SBase at rank %s" +721(E) : "Error %sing task %s on SBase at rank %s" +723(W) : "Task binary directory path to SBase at rank %s is empty. Current working directory will be used" +724(E) : "No task %s on SBase at rank %s" +725(E) : "Error attempting to set state for task %s on SBase at rank %s" 801(D) : "P_builder::Add(name=%s,file=%s)" 802(D) : "Removing task %s from the Orchestrator" 803(F) : "P_builder:out of memory to create a QCoreApplication object" 804(F) : "P_builder:out of memory to create the graph parser. Shutting down" 805(E) : "P_builder:compile failed. Errors dumped to make_errs.txt" -806(W) : "P_builder:unable to read binary %s. Application may not be completely loaded. %s" -807(E) : "Unable to copy files to directory %s. Check directory exists and has access rights. %s" +806(W) : "P_builder:unable to read binary %s. Application may not be completely loaded" +807(E) : "Unable to copy files to directory %s. Check directory exists and has access rights" 808(W) : "Invalid or nonexistent definition file %s for task %s. Task not loaded" 809(W) : "Tasks from specification %s already loaded - command ignored" 810(E) : "DeviceType %s too big: requires %s bytes but max capacity is %s" @@ -135,11 +139,6 @@ 813(E) : "Error: task %s could not be %s. State on Mothership is %s" 814(W) : "P_builder: no task found with source file %s" 815(W) : "P_builder: task %s not registered under source file %s" -816(E) : "P_Builder: Cannot open %s for writing: %s" -817(E) : "Unable to remove directory %s: %s" -818(E) : "Unable to create directory %s: %s" - - 901(U) : "%s: %s corrupt ?" 902(U) : "OrchBase::ClearTasks(tt=%s)" 903(U) : "OrchBase::ClearDcls: Null pointer to %s in P_typdclm" From 5336f1c1fe8b51edd6fb1c99cd581506fcb49e4d Mon Sep 17 00:00:00 2001 From: AlexRast Date: Mon, 29 Jul 2019 00:22:30 +0100 Subject: [PATCH 03/14] added distribution functions and a dummy Mothership for testing --- Build/gcc/Makefile | 9 +- Build/gcc/Makefile.executable_prerequisites | 44 ++- Source/Common/CommonBase.h | 2 +- Source/Mothership/MothershipDummy.cpp | 253 ++++++++++++++ Source/Mothership/MothershipDummy.h | 49 +++ Source/Mothership/MothershipDummyMain.cpp | 12 + Source/Mothership/TMoth.cpp | 3 + Source/NameServer/AddressBook.cpp | 1 + Source/NameServer/AddressBook.hpp | 1 + Source/NameServer/NameServer.cpp | 354 ++++++++++++++++---- Source/NameServer/NameServer.h | 4 +- Source/NameServer/SBase.cpp | 261 ++++++++------- Source/NameServer/SBase.h | 8 +- Source/OrchBase/CMsg_p.cpp | 23 +- Source/OrchBase/CMsg_p.h | 9 +- Source/OrchBase/OrchBaseLink.cpp | 2 +- Source/OrchBase/OrchBaseTask.cpp | 47 +-- Source/OrchestratorMessages.txt | 5 + 18 files changed, 840 insertions(+), 247 deletions(-) mode change 100644 => 100755 Build/gcc/Makefile.executable_prerequisites create mode 100644 Source/Mothership/MothershipDummy.cpp create mode 100644 Source/Mothership/MothershipDummy.h create mode 100644 Source/Mothership/MothershipDummyMain.cpp diff --git a/Build/gcc/Makefile b/Build/gcc/Makefile index 3d77a004..2d2834c7 100644 --- a/Build/gcc/Makefile +++ b/Build/gcc/Makefile @@ -109,9 +109,10 @@ NAMESERVER_EXECUTABLE = $(EXECUTABLE_DIR)/nameserver RTCL_EXECUTABLE = $(EXECUTABLE_DIR)/rtcl INJECTOR_EXECUTABLE = $(EXECUTABLE_DIR)/injector MOTHERSHIP_EXECUTABLE = $(EXECUTABLE_DIR)/mothership +MOTHERSHIP_DUMMY_EXECUTABLE = $(EXECUTABLE_DIR)/mothershipdummy all: orchestrate root dummy logserver nameserver rtcl injector mothership \ - application_staging_environment + mothershipdummy application_staging_environment orchestrate: $(ORCHESTRATE_SCRIPT) root: $(ROOT_EXECUTABLE) dummy: $(DUMMY_EXECUTABLE) @@ -120,6 +121,7 @@ nameserver: $(NAMESERVER_EXECUTABLE) rtcl: $(RTCL_EXECUTABLE) injector: $(INJECTOR_EXECUTABLE) mothership: $(MOTHERSHIP_EXECUTABLE) +mothershipdummy: $(MOTHERSHIP_DUMMY_EXECUTABLE) # Prerequisites (objects) for building executables are defined here: include Makefile.executable_prerequisites @@ -135,7 +137,8 @@ tests: $(ALL_TESTS) $(TEST_SCRIPT) # Makefile.executable_prerequisites for the prerequisites of these executables # (obviously...) $(ROOT_EXECUTABLE) $(DUMMY_EXECUTABLE) $(LOGSERVER_EXECUTABLE) \ -$(NAMESERVER_EXECUTABLE) $(RTCL_EXECUTABLE) $(INJECTOR_EXECUTABLE): +$(NAMESERVER_EXECUTABLE) $(RTCL_EXECUTABLE) $(INJECTOR_EXECUTABLE) \ +$(MOTHERSHIP_DUMMY_EXECUTABLE): @$(shell $(MKDIR) $(EXECUTABLE_DIR)) $(CXX) -pthread -Wl,-rpath-link=$(QT_LIB_DIR) \ -L$(MPICH_LIB_DIR) -L$(QT_LIB_DIR) -L/usr/lib \ @@ -285,7 +288,7 @@ clean: application_staging_environment_teardown # Non-builtin targets that do not explicitly represent files that are created. .PHONY: all clean debug tests orchestrate root dummy logserver nameserver rtcl injector \ - mothership application_staging_environment \ + mothership mothershipdummy application_staging_environment \ application_staging_environment_teardown # The "all" target should be the default. diff --git a/Build/gcc/Makefile.executable_prerequisites b/Build/gcc/Makefile.executable_prerequisites old mode 100644 new mode 100755 index 0ceeb8e9..5e486266 --- a/Build/gcc/Makefile.executable_prerequisites +++ b/Build/gcc/Makefile.executable_prerequisites @@ -75,6 +75,15 @@ HARDWARE_FILE_PARSER_SOURCES = $(SOURCE_DIR)/OrchBase/HardwareFileManager/Hardwa $(GENERICS_DIR)/uif.cpp \ $(HARDWARE_DEPLOYER_SOURCES) +# Sources used by executables that provide POETS name resolution. +NAME_SERVICES_SOURCES = $(SOURCE_DIR)/NameServer/ABDefs.cpp \ + $(SOURCE_DIR)/NameServer/ABRecord.cpp \ + $(SOURCE_DIR)/NameServer/ABTask.cpp \ + $(SOURCE_DIR)/NameServer/AddressBook.cpp \ + $(SOURCE_DIR)/NameServer/SBase.cpp \ + $(SOURCE_DIR)/OrchBase/CMsg_p.cpp \ + $(SOURCE_DIR)/OrchBase/P_addr.cpp + # The LogServer message file is a static text file LOGSERVER_MESSAGE_FILE_ORIGIN = $(SOURCE_DIR)/OrchestratorMessages.txt @@ -160,22 +169,19 @@ LOGSERVER_SOURCES += $(TRULY_COMMON_SOURCES) # The nameserver component consists of: # -# - The "main" file (/Source/NameServer/NameServerMain.cpp) and its dependencies -# in /Source/NameServer. -# - TODO: Migrate SBase and AddressBook into a common directory and source group +# - The "main" file (/Source/NameServer/NameServerMain.cpp) and its derived +# class in /Source/NameServer. +# +# - The name services sources in /Source/NameServer. # # - The truly common sources. # # - The hardware and software address sources. # NAMESERVER_SOURCES = $(SOURCE_DIR)/NameServer/NameServerMain.cpp \ - $(SOURCE_DIR)/NameServer/NameServer.cpp \ - $(SOURCE_DIR)/NameServer/ABDefs.cpp \ - $(SOURCE_DIR)/NameServer/ABRecord.cpp \ - $(SOURCE_DIR)/NameServer/ABTask.cpp \ - $(SOURCE_DIR)/NameServer/AddressBook.cpp \ - $(SOURCE_DIR)/NameServer/SBase.cpp + $(SOURCE_DIR)/NameServer/NameServer.cpp +NAMESERVER_SOURCES += $(NAME_SERVICES_SOURCES) NAMESERVER_SOURCES += $(TRULY_COMMON_SOURCES) #NAMESERVER_SOURCES += $(SOFTWARE_ADDRESS_SOURCES) #NAMESERVER_SOURCES += $(HARDWARE_ADDRESS_SOURCES) @@ -250,6 +256,22 @@ MOTHERSHIP_SOURCES = $(SOURCE_DIR)/Mothership/MothershipMain.cpp \ MOTHERSHIP_SOURCES += $(TRULY_COMMON_SOURCES) +# A dummy mothership is an executable to provide mothership-like functionality +# when there is no connected box with a HostLink interface. It consists of: +# +# - The "main" file (/Source/Mothership/MothershipDummyMain.cpp) and its +# dependencies in /Source/Mothership +# +# - The name services sources. +# +# - The truly common sources. +# +MOTHERSHIP_DUMMY_SOURCES = $(SOURCE_DIR)/Mothership/MothershipDummyMain.cpp \ + $(SOURCE_DIR)/Mothership/MothershipDummy.cpp + +MOTHERSHIP_DUMMY_SOURCES += $(NAME_SERVICES_SOURCES) +MOTHERSHIP_DUMMY_SOURCES += $(TRULY_COMMON_SOURCES) + # Convert these lists of sources into a list of objects to define a dependency # system for the linker. @@ -281,7 +303,7 @@ $(1)_OBJECTS := $(call orch_sources_to_objects, $($(1)_SOURCES)) endef $(foreach object_set,\ - ROOT INJECTOR DUMMY LOGSERVER NAMESERVER RTCL MOTHERSHIP,\ + ROOT INJECTOR DUMMY LOGSERVER NAMESERVER RTCL MOTHERSHIP MOTHERSHIP_DUMMY,\ $(eval $(call OBJECT_TEMPLATE,$(object_set)))) # Define executable prerequisites. RULE_TEMPLATE defines the substitutions made @@ -294,5 +316,5 @@ $($(1)_EXECUTABLE): $($(1)_OBJECTS) endef $(foreach executable_name,\ - ROOT DUMMY LOGSERVER NAMESERVER RTCL INJECTOR MOTHERSHIP,\ + ROOT DUMMY LOGSERVER NAMESERVER RTCL INJECTOR MOTHERSHIP MOTHERSHIP_DUMMY,\ $(eval $(call RULE_TEMPLATE,$(executable_name)))) diff --git a/Source/Common/CommonBase.h b/Source/Common/CommonBase.h index 9680d95a..84cd069a 100644 --- a/Source/Common/CommonBase.h +++ b/Source/Common/CommonBase.h @@ -70,7 +70,7 @@ string Ssource; string Sbinary; string STIME; string SDATE; -vector pPmap; +vector pPmap; // if Comms is a map, this must be too int MPI_provided; pthread_t MPI_accept; diff --git a/Source/Mothership/MothershipDummy.cpp b/Source/Mothership/MothershipDummy.cpp new file mode 100644 index 00000000..e733fba6 --- /dev/null +++ b/Source/Mothership/MothershipDummy.cpp @@ -0,0 +1,253 @@ +#include "MothershipDummy.h" +#include "CMsg_p.h" +#include "Pglobals.h" +#include + + +MothershipDummy::MothershipDummy(int argc,char * argv[], string d) : + SBase(argc,argv,d,string(__FILE__)) +{ + FnMapx.push_back(new FnMap_t); // create a default function table + // Load the message map + + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL)] = &MothershipDummy::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &MothershipDummy::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &MothershipDummy::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &MothershipDummy::OnCmnd; + (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &MothershipDummy::OnCmnd; + (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &MothershipDummy::OnCmnd; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DIST )] = &MothershipDummy::OnName; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RECL )] = &MothershipDummy::OnName; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::TDIR )] = &MothershipDummy::OnName; + (*FnMapx[0])[PMsg_p::KEY(Q::SUPR )] = &MothershipDummy::OnSuper; + (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &MothershipDummy::OnSyst; + (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &MothershipDummy::OnSyst; + (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &MothershipDummy::OnSyst; + (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &MothershipDummy::OnSyst; + + MPISpinner(); // Spin on MPI messages; exit only on DIE +} + +MothershipDummy::~MothershipDummy() +{ + WALKVECTOR(FnMap_t*,FnMapx,F) // WALKVECTOR and WALKMAP are in macros.h (long include chain) + delete *F; // get rid of derived class function tables +} + +unsigned MothershipDummy::Connect(string svc) +{ + unsigned connErr = MPI_SUCCESS; + // set up the connection in the base class + if ((connErr = SBase::Connect(svc)) != MPI_SUCCESS) return connErr; + FnMapx.push_back(new FnMap_t); // add another function table in the derived class + int fIdx=FnMapx.size()-1; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL)] = &MothershipDummy::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &MothershipDummy::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &MothershipDummy::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &MothershipDummy::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &MothershipDummy::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &MothershipDummy::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DIST )] = &MothershipDummy::OnName; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RECL )] = &MothershipDummy::OnName; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::TDIR )] = &MothershipDummy::OnName; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SUPR )] = &MothershipDummy::OnSuper; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &MothershipDummy::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &MothershipDummy::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &MothershipDummy::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &MothershipDummy::OnSyst; + return MPI_SUCCESS; +} + +unsigned MothershipDummy::CmLoad(string task) +{ + return 0; +} + +unsigned MothershipDummy::CmRun(string task) +{ + return 0; +} + +unsigned MothershipDummy::CmStop(string task) +{ + return 0; +} + +unsigned MothershipDummy::OnCfg(PMsg_p *msg, unsigned comm) +{ + switch(msg->L(2)) + { + case Q::DIST: + return ConfigDistribute(msg, comm); + case Q::TDIR: + return SBase::ConfigDir(msg, comm); + case Q::BLD: + return SBase::ConfigBuild(msg, comm); + case Q::RECL: + return ConfigRecall(msg, comm); + case Q::DEL: + return SBase::ConfigDelete(msg, comm); + case Q::STATE: + return ConfigState(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +unsigned MothershipDummy::OnCmnd(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned MothershipDummy::OnDump(PMsg_p *msg, unsigned comm) +{ + if (msg->L(2) == Q::LIST) return DumpSummary(msg, comm); + if (msg->L(2) != Q::TASK) + { + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } + switch(msg->L(3)) + { + case Q::ALL: + return DumpAll(msg, comm); + case Q::NM: + return DumpTask(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} + +unsigned MothershipDummy::OnName(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned MothershipDummy::OnSuper(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned MothershipDummy::OnSyst(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned MothershipDummy::SystHW(const vector& args) +{ + return 0; +} + +unsigned MothershipDummy::SystKill() +{ + return 0; +} + +unsigned MothershipDummy::SystShow() +{ + return 0; +} + +unsigned MothershipDummy::SystTopo() +{ + return 0; +} + +void MothershipDummy::Dump(FILE *fp, string task) +{ + if (task.empty()) + { + fprintf(fp,"MothershipDummy summary dump+++++++++++++++++++++++++++\n"); + SBase::Dump(fp); + fprintf(fp,"MothershipDummy summary dump---------------------------\n"); + } + else + { + fprintf(fp,"MothershipDummy dump+++++++++++++++++++++++++++++++++++\n"); + SBase::Dump(fp,task); + fprintf(fp,"Mothership dump-----------------------------------\n"); + } + CommonBase::Dump(fp); +} + +unsigned MothershipDummy::ConfigDistribute(PMsg_p *msg, unsigned comm) +{ + unsigned err = SUCCESS; + if (err = SBase::ConfigDir(msg,comm)) return err; + if (err = SBase::ConfigDistribute(msg,comm)) return err; + string taskName = msg->Zname(0); + // temporarily we dump everything out to a file --------------------------------------------- + FILE *dumpFile = fopen("MothershipDummy_dump.txt","w"); + fprintf(dumpFile, "------------------------------Dummy Mothership Dump-----------------------------\n"); + int aCount = 0; + int* mAddr = msg->Get(0,aCount); + // SymAddr_t* mAddr = msg->Get(0,aCount); + if (!mAddr) Post(730,"NAME::CFG::DIST",int2str(Urank),"Mothership address"); + fprintf(dumpFile, "Mothership Address: %d\n", mAddr ? *mAddr : 0); + CMsg_p distMsg(*msg); + distMsg.Dump(dumpFile); + Dump(dumpFile,taskName); + fclose(dumpFile); + //-------------------------------------------------------------------------------------------- + return err; +} + +unsigned MothershipDummy::ConfigRecall(PMsg_p *msg, unsigned comm) +{ + string taskName = msg->Zname(0); + vector tasks; + unsigned err = SUCCESS; + if (taskName.empty()) + { + if (err = ListTask(tasks)) + { + Post(721,"gett","list",int2str(Urank)); + return err; + } + } + else tasks.push_back(taskName); + vector::iterator task; + for (task = tasks.begin(); task != tasks.end(); task++) + { + if (err = DelTask(*task)) + { + Post(721,"delet",*task,int2str(Urank)); + err = ERR_NONFATAL; + } + } + return err; +} + +unsigned MothershipDummy::ConfigState(PMsg_p *msg, unsigned comm) +{ + return 0; +} + +unsigned MothershipDummy::DumpAll(PMsg_p * msg, unsigned comm) +{ + return SBase::DumpAll(msg, comm); +} + +unsigned MothershipDummy::DumpSummary(PMsg_p * msg, unsigned comm) +{ + return SBase::DumpSummary(msg, comm); +} + +unsigned MothershipDummy::DumpTask(PMsg_p * msg, unsigned comm) +{ + return SBase::DumpTask(msg, comm); +} diff --git a/Source/Mothership/MothershipDummy.h b/Source/Mothership/MothershipDummy.h new file mode 100644 index 00000000..e09bbbc3 --- /dev/null +++ b/Source/Mothership/MothershipDummy.h @@ -0,0 +1,49 @@ +#ifndef __MothershipDummy_H__ +#define __MothershipDummy_H__ + +#include "SBase.h" +#include "PMsg_p.hpp" +#include + +class MothershipDummy : public SBase +{ + public: + MothershipDummy(int,char **,string); + virtual ~MothershipDummy(); + + unsigned Connect(string=""); + + private: + + unsigned CmLoad(string); + unsigned CmRun(string); + unsigned CmStop(string); + unsigned OnCfg(PMsg_p *, unsigned); + unsigned OnCmnd(PMsg_p *,unsigned); + unsigned OnDump(PMsg_p *, unsigned); + unsigned OnName(PMsg_p *,unsigned); + unsigned OnSuper(PMsg_p *,unsigned); + unsigned OnSyst(PMsg_p *,unsigned); + unsigned SystHW(const vector&); + unsigned SystKill(); + unsigned SystShow(); + unsigned SystTopo(); + + #include "SDecode.cpp" + void Dump(FILE * = stdout, string = ""); + + // unsigned ConfigDir(PMsg_p *, unsigned); + unsigned ConfigDistribute(PMsg_p *, unsigned); + unsigned ConfigRecall(PMsg_p *, unsigned); + unsigned ConfigState(PMsg_p *, unsigned); + unsigned DumpAll(PMsg_p *, unsigned); + unsigned DumpSummary(PMsg_p *, unsigned); + unsigned DumpTask(PMsg_p *, unsigned); + + typedef unsigned (MothershipDummy::*pMeth)(PMsg_p *, unsigned); + typedef map FnMap_t; + vector FnMapx; + +}; + +#endif diff --git a/Source/Mothership/MothershipDummyMain.cpp b/Source/Mothership/MothershipDummyMain.cpp new file mode 100644 index 00000000..b2eb31e5 --- /dev/null +++ b/Source/Mothership/MothershipDummyMain.cpp @@ -0,0 +1,12 @@ +#include "MothershipDummy.h" +#include "Pglobals.h" +#include + +int main(int argc, char * argv[]) +{ + MothershipDummy* mothership = new MothershipDummy(argc, argv, string(csMOTHERSHIPproc)); + delete mothership; + printf("%s Main closing down\n", csMOTHERSHIPproc); + fflush(stdout); + return 0; +} diff --git a/Source/Mothership/TMoth.cpp b/Source/Mothership/TMoth.cpp index 2cf14b51..eef828b8 100644 --- a/Source/Mothership/TMoth.cpp +++ b/Source/Mothership/TMoth.cpp @@ -67,6 +67,9 @@ WALKMAP(uint32_t,PinBuf_t*,TwigMap,D) } WALKMAP(string,TaskInfo_t*,TaskMap,T) delete T->second; + +WALKVECTOR(FnMap_t*,FnMapx,F) + delete *F; // get rid of derived class function tables } //------------------------------------------------------------------------------ diff --git a/Source/NameServer/AddressBook.cpp b/Source/NameServer/AddressBook.cpp index 45c00759..c906099c 100644 --- a/Source/NameServer/AddressBook.cpp +++ b/Source/NameServer/AddressBook.cpp @@ -14,6 +14,7 @@ namespace AddressBookNS // static constant definitions const unsigned AddressBook::SUCCESS; +const unsigned AddressBook::ERR_NONFATAL; const unsigned AddressBook::ERR_INVALID_TASK; const unsigned AddressBook::ERR_INVALID_DEVTYPE; const unsigned AddressBook::ERR_INVALID_DEVICE; diff --git a/Source/NameServer/AddressBook.hpp b/Source/NameServer/AddressBook.hpp index cc27b3cd..f2c60654 100644 --- a/Source/NameServer/AddressBook.hpp +++ b/Source/NameServer/AddressBook.hpp @@ -123,6 +123,7 @@ class AddressBook static const long ERR_BAD_COUNT = -1; static const unsigned SUCCESS = 0; + static const unsigned ERR_NONFATAL = 0; static const unsigned ERR_INVALID_TASK = 1; static const unsigned ERR_INVALID_DEVTYPE = 2; static const unsigned ERR_INVALID_DEVICE = 3; diff --git a/Source/NameServer/NameServer.cpp b/Source/NameServer/NameServer.cpp index 03a09a9b..e7313df1 100644 --- a/Source/NameServer/NameServer.cpp +++ b/Source/NameServer/NameServer.cpp @@ -6,6 +6,7 @@ #include "Pglobals.h" // #include "Ns_el.h" // #include "jnj.h" +#include "CMsg_p.h" #include //============================================================================== @@ -15,15 +16,15 @@ NameServer::NameServer(int argc,char * argv[],string d): { FnMapx.push_back(new FnMap_t); // create a default function table // Load the message map - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &NameServer::OnCfg; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &NameServer::OnCfg; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &NameServer::OnCfg; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &NameServer::OnCfg; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &NameServer::OnCfg; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &NameServer::OnCfg; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL)] = &NameServer::OnDump; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &NameServer::OnDump; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &NameServer::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &NameServer::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &NameServer::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &NameServer::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &NameServer::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &NameServer::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &NameServer::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL)] = &NameServer::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &NameServer::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &NameServer::OnDump; MPISpinner(); // Spin on MPI messages; exit only on DIE @@ -34,11 +35,34 @@ NameServer::NameServer(int argc,char * argv[],string d): NameServer::~NameServer() { + WALKVECTOR(FnMap_t*,FnMapx,F) // WALKVECTOR and WALKMAP are in macros.h (long include chain) + delete *F; // get rid of derived class function tables //printf("********* NameServer rank %d destructor\n",Urank); fflush(stdout); } //------------------------------------------------------------------------------ +unsigned NameServer::Connect(string svc) +{ + unsigned connErr = MPI_SUCCESS; + // set up the connection in the base class + if ((connErr = SBase::Connect(svc)) != MPI_SUCCESS) return connErr; + FnMapx.push_back(new FnMap_t); // add another function table in the derived class + int fIdx=FnMapx.size()-1; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &NameServer::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &NameServer::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &NameServer::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &NameServer::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &NameServer::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &NameServer::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL)] = &NameServer::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &NameServer::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &NameServer::OnDump; + return MPI_SUCCESS; +} + +//------------------------------------------------------------------------------ + void NameServer::Dump(FILE * fp, string task) { if (task.empty()) @@ -84,7 +108,7 @@ unsigned NameServer::OnCfg(PMsg_p * msg, unsigned comm) unsigned NameServer::OnDump(PMsg_p *msg, unsigned comm) { - if (msg->L(2) == Q::LIST) return DumpSummary(msg); + if (msg->L(2) == Q::LIST) return DumpSummary(msg, comm); if (msg->L(2) != Q::TASK) { Post(700,uint2str(msg->Key()),int2str(Urank)); @@ -93,9 +117,9 @@ unsigned NameServer::OnDump(PMsg_p *msg, unsigned comm) switch(msg->L(3)) { case Q::ALL: - return DumpAll(msg); + return DumpAll(msg, comm); case Q::NM: - return DumpTask(msg); + return DumpTask(msg, comm); default: Post(700,uint2str(msg->Key()),int2str(Urank)); return 0; @@ -106,12 +130,258 @@ unsigned NameServer::OnDump(PMsg_p *msg, unsigned comm) unsigned NameServer::ConfigDir(PMsg_p * msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + const vector* supervisors; + unsigned err = GetSupervisors(taskName, supervisors); + if (err == AddressBook::ERR_TASK_NOT_FOUND) + { + Post(724,taskName,int2str(Urank)); + return err; + } + if (err == AddressBook::ERR_INVALID_MAP) + { + Post(727,int2str(Urank)); + return ERR_NONFATAL; + } + // finding supervisors naively by rank starting with comm 0 will work for + // the moment but this should be fixed to permit arbitrary lookup by comm. + for (vector::const_iterator s = supervisors->begin(); s != supervisors->end(); s++) + { + for (unsigned sComm = 0; sComm < pPmap.size(); sComm++) + { + WALKVECTOR(int,pPmap[sComm]->U.Mothership,m) + { + if (*m == static_cast(s->Rank)) + { + msg->comm = Comms[sComm]; // relay the message to all supervisors + msg->Send(*m); // associated with this task. + break; + } + } + if (msg->Tgt() == s->Rank) break; // supervisor found + } + if (msg->Tgt() != s->Rank) // supervisor not found + { + Post(728,int2str(Urank),uint2str(s->Rank)); + return ERR_INVALID_SUPERVISOR; + } + } + return SBase::ConfigDir(msg,comm); } unsigned NameServer::ConfigDistribute(PMsg_p * msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + int rcount = 0; + // SymAddr_t superAddr = msg->Get(0,rcount); // use once symbolic addresses are implemented + int* superAddr = msg->Get(0,rcount); + if (!superAddr) + { + Post(730, "NAME::CFG::DIST",int2str(Urank),"Mothership Address"); + return ERR_NONFATAL; + } + const vector* supervisors; + unsigned err = GetSupervisors(taskName, supervisors); + if (err == AddressBook::ERR_TASK_NOT_FOUND) + { + Post(724,taskName,int2str(Urank)); + return err; + } + if (err == AddressBook::ERR_INVALID_MAP) + { + Post(727,int2str(Urank)); + return ERR_NONFATAL; + } + // finding supervisors naively by rank starting with comm 0 will work for + // the moment but this should be fixed to permit arbitrary lookup by comm. + for (vector::const_iterator s = supervisors->begin(); s != supervisors->end(); s++) + { + for (unsigned sComm = 0; sComm < pPmap.size(); sComm++) + { + WALKVECTOR(int,pPmap[sComm]->U.Mothership,m) + { + // if ((s->Address == *superAddr) && (*m == static_cast(s->Rank))) // SymAddr_t form + if ((static_cast(s->Rank) == *superAddr) && (*m == static_cast(s->Rank))) + { + // send over the task NameServer data + PMsg_p taskInfo(Comms[sComm]); + taskInfo.Src(Urank); + taskInfo.Key(Q::NAME,Q::DATA,Q::TASK); + TaskData_t taskData; + if (err = GetTask(taskName,taskData)) + { + Post(724,taskName,int2str(Urank)); + return err; + } + vector devCounts; + devCounts.push_back(taskData.DeviceCount); + devCounts.push_back(taskData.ExternalCount); + taskInfo.Zname(0,taskData.Name); + taskInfo.Zname(1,taskData.Path); + taskInfo.Zname(2,taskData.XML); + taskInfo.PutX(0,&taskData.MessageTypes); + taskInfo.PutX(1,&taskData.AttributeTypes); + taskInfo.Put(2,&devCounts); + taskInfo.Send(*m); + taskInfo.Key(Q::NAME,Q::DATA,Q::DEVT); + WALKVECTOR(DevTypeRecord_t,taskData.DeviceTypes,devType) + { + taskInfo.Zname(2,devType->Name); + taskInfo.Put(0,&devType->InMsgs); + taskInfo.Put(1,&devType->OuMsgs); + taskInfo.Send(*m); + } + vectordevNames; + vectordevAddrs; + vectordevAttrs; + vector superRanks; + // send over the Supervisor device info + taskInfo.Key(Q::NAME,Q::DATA,Q::SUPV); + devNames.push_back(s->Name); + devAddrs.push_back(s->Address); + devAttrs.push_back(s->Attribute); + superRanks.push_back(s->Rank); + taskInfo.Zname(2,taskData.DeviceTypes[s->DeviceType].Name); + taskInfo.PutX(0,&devNames); + taskInfo.Put(1,&devAddrs); + taskInfo.Put(2,&devAttrs); + taskInfo.Put(3,&superRanks); + taskInfo.Send(*m); + devNames.clear(); + devAddrs.clear(); + devAttrs.clear(); + superRanks.clear(); + const RecordVect_t* devicesThisSuper; + if (err = FindBySuper(taskName,s->Address,devicesThisSuper)) + { + switch (err) + { + case ERR_TASK_NOT_FOUND: + Post(724,taskName,int2str(Urank)); + return err; + case ERR_INVALID_MAP: + Post(727,int2str(Urank)); + return ERR_NONFATAL; + case ERR_DEVICE_NOT_FOUND: + Post(718,taskName,int2str(Urank)); + return err; + default: + Post(136); + return err; + } + } + if (devicesThisSuper->size()) + { + RecordType_t devRType((*devicesThisSuper->begin())->RecordType); + DTypeIdx devTypeIndex = (*devicesThisSuper->begin())->DeviceType; + taskInfo.Zname(2,taskData.DeviceTypes[devTypeIndex].Name); + switch(devRType) + { + case Device: + taskInfo.Key(Q::NAME,Q::DATA,Q::DEVI); + break; + case DeviceExt: + taskInfo.Key(Q::NAME,Q::DATA,Q::DEVE); + break; + case External: + taskInfo.Key(Q::NAME,Q::DATA,Q::EXTN); + break; + case Supervisor: + taskInfo.Key(Q::NAME,Q::DATA,Q::SUPV); + } + for (RecordVect_t::const_iterator dR = devicesThisSuper->begin(); dR != devicesThisSuper->end(); dR++) + { + if (((*dR)->DeviceType != devTypeIndex) || ((*dR)->RecordType != devRType)) + { + if ((devRType == Device) || (devRType == DeviceExt)) + { + /* awkwardly, because GetSupervisors returns a const vector*, + internal members are const-qualified. But the Put functionality expects + non-const objects when creating the data stream (because the stream itself + is non-const and it uses an initialiser form to stream the data in). In + turn this means we have to create a temporary copy of the Supervisor + address for no other reason than to be able to heave it into another + buffer. + */ + SymAddr_t supAddr = s->Address; + taskInfo.Put(3,&supAddr); + } + taskInfo.PutX(0,&devNames); + taskInfo.Put(1,&devAddrs); + taskInfo.Put(2,&devAttrs); + taskInfo.Send(*m); + devTypeIndex = (*dR)->DeviceType; + taskInfo.Zname(2,taskData.DeviceTypes[devTypeIndex].Name); + devRType = (*dR)->RecordType; + switch(devRType) + { + case Device: + taskInfo.Key(Q::NAME,Q::DATA,Q::DEVI); + break; + case DeviceExt: + taskInfo.Key(Q::NAME,Q::DATA,Q::DEVE); + break; + case External: + taskInfo.Key(Q::NAME,Q::DATA,Q::EXTN); + break; + case Supervisor: + taskInfo.Key(Q::NAME,Q::DATA,Q::SUPV); + } + devNames.clear(); + devAddrs.clear(); + devAttrs.clear(); + superRanks.clear(); + } + if ((*dR)->RecordType == Supervisor) + { + if ((*dR)->Address != s->Address) + { + Post(729,(*dR)->Name,s->Name); + return ERR_INVALID_SUPERVISOR; + } + superRanks.push_back((*dR)->Rank); + taskInfo.Put(3,&superRanks); + } + devNames.push_back((*dR)->Name); + devAddrs.push_back((*dR)->Address); + devAttrs.push_back((*dR)->Attribute); + } + if ((devRType == Device) || (devRType == DeviceExt)) + { + SymAddr_t supAddr = s->Address; + taskInfo.Put(3,&supAddr); + } + taskInfo.PutX(0,&devNames); + taskInfo.Put(1,&devAddrs); + taskInfo.Put(2,&devAttrs); + taskInfo.Send(*m); + } + msg->comm = Comms[sComm]; // relay the message to the supervisor + msg->Send(*m); // being deployed to in this message + break; + } + } + if (msg->Tgt() == s->Rank) break; // supervisor found + } + if (msg->Tgt() != s->Rank) // supervisor not found + { + Post(728,int2str(Urank),uint2str(s->Rank)); + return ERR_INVALID_SUPERVISOR; + } + else break; // distribute is one supervisor at a time + } + // debug output ----------------------------------- + FILE* nsOutput = fopen("DistributionDump.txt","w"); + CMsg_p distMsg(*msg); + distMsg.Dump(nsOutput); + fclose(nsOutput); + //------------------------------------------------- + if (err = SBase::ConfigDir(msg,comm)) // distribution messages contain the binary directory + { + Post(724,taskName,int2str(Urank)); // no task of this name. + return err; // this should have been caught earlier. + } + return SBase::ConfigDistribute(msg, comm); } unsigned NameServer::ConfigRecall(PMsg_p * msg, unsigned comm) @@ -124,61 +394,5 @@ unsigned NameServer::ConfigState(PMsg_p * msg, unsigned comm) return 0; } -//------------------------------------------------------------------------------ - -unsigned NameServer::DumpAll(PMsg_p *msg) -{ - // filename must be treated as an absolute literal, because tasks could in - // general have different paths, therefore there is no way to infer what - // additional path string might be appended to the file name give. - string filename = msg->Zname(1); - FILE* dumpFile; - if (filename.empty()) dumpFile = stdout; - else dumpFile = fopen(filename.c_str(), "a"); - vector tasks; - unsigned err = AddressBook::SUCCESS; - if (err = ListTask(tasks)) - { - Post(709, int2str(Urank)); - if (dumpFile != stdout) fclose(dumpFile); - return err; - } - WALKVECTOR(string, tasks, task) Dump(dumpFile, *task); - return err; -} - -//------------------------------------------------------------------------------ - -unsigned NameServer::DumpSummary(PMsg_p *msg) -{ - string filename = msg->Zname(1); - FILE* dumpFile; - if (filename.empty()) dumpFile = stdout; - else dumpFile = fopen(filename.c_str(), "a"); - Dump(dumpFile); - if (dumpFile != stdout) fclose(dumpFile); - return AddressBook::SUCCESS; -} - -//------------------------------------------------------------------------------ - -unsigned NameServer::DumpTask(PMsg_p *msg) -{ - string taskName = msg->Zname(0); - if (taskName.empty()) - { - Post(710, int2str(Urank)); - return AddressBook::ERR_INVALID_TASK; - } - string filename = msg->Zname(1); - FILE* dumpFile; - if (filename.empty()) dumpFile = stdout; - else dumpFile = fopen(filename.c_str(), "a"); - Dump(dumpFile, taskName); - if (dumpFile != stdout) fclose(dumpFile); - return AddressBook::SUCCESS; -} - - //============================================================================== diff --git a/Source/NameServer/NameServer.h b/Source/NameServer/NameServer.h index 1c4ba6a8..31a8b9da 100644 --- a/Source/NameServer/NameServer.h +++ b/Source/NameServer/NameServer.h @@ -13,6 +13,7 @@ class NameServer : public SBase NameServer(int,char **,string); virtual ~ NameServer(); + unsigned Connect (string=""); unsigned OnCfg (PMsg_p *, unsigned); unsigned OnDump (PMsg_p *, unsigned); @@ -24,9 +25,6 @@ virtual ~ NameServer(); unsigned ConfigDistribute (PMsg_p *, unsigned); unsigned ConfigRecall (PMsg_p *, unsigned); unsigned ConfigState (PMsg_p *, unsigned); - unsigned DumpAll (PMsg_p *); - unsigned DumpSummary (PMsg_p *); - unsigned DumpTask (PMsg_p *); typedef unsigned (NameServer::*pMeth)(PMsg_p *, unsigned); typedef map FnMap_t; diff --git a/Source/NameServer/SBase.cpp b/Source/NameServer/SBase.cpp index 050086b1..24d8de69 100644 --- a/Source/NameServer/SBase.cpp +++ b/Source/NameServer/SBase.cpp @@ -6,69 +6,71 @@ SBase::SBase(int argc,char ** argv,string d,string s): CommonBase(argc,argv,d,s) { FnMapx.push_back(new FnMap_t); // create a default function table (for the *nameserver base* class!) // Load event handler map - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NM )] = &SBase::OnSend; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ID )] = &SBase::OnSend; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ALL )] = &SBase::OnSend; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NGRP)] = &SBase::OnSend; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::IGRP)] = &SBase::OnSend; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NSUP)] = &SBase::OnSend; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ISUP)] = &SBase::OnSend; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::SUPV )] = &SBase::OnSend; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::EXTN )] = &SBase::OnSend; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::NM )] = &SBase::OnSend; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::IN )] = &SBase::OnSend; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::OUT )] = &SBase::OnSend; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::SEND,Q::ATTR )] = &SBase::OnSend; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NM )] = &SBase::OnQuery; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ID )] = &SBase::OnQuery; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ALL )] = &SBase::OnQuery; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NGRP )] = &SBase::OnQuery; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::IGRP )] = &SBase::OnQuery; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NSUP )] = &SBase::OnQuery; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ISUP )] = &SBase::OnQuery; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::SUPV )] = &SBase::OnQuery; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::EXTN )] = &SBase::OnQuery; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::NM )] = &SBase::OnQuery; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::IN )] = &SBase::OnQuery; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::OUT )] = &SBase::OnQuery; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::QRY,Q::ATTR )] = &SBase::OnQuery; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &SBase::OnCfg; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &SBase::OnCfg; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &SBase::OnCfg; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &SBase::OnCfg; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &SBase::OnCfg; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &SBase::OnCfg; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NF )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::TNF )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::TNF )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::TNF )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::TNF )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::TNF )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::LIST )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::TASK )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::TASK,Q::TNF )] = &SBase::OnReply; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DATA,Q::TASK )] = &SBase::OnData; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DATA,Q::DEVT )] = &SBase::OnData; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DATA,Q::DEVI )] = &SBase::OnData; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DATA,Q::DEVE )] = &SBase::OnData; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DATA,Q::EXTN )] = &SBase::OnData; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DATA,Q::SUPV )] = &SBase::OnData; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::ON )] = &SBase::OnCmd; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::OFF )] = &SBase::OnCmd; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::ON )] = &SBase::OnCmd; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::OFF )] = &SBase::OnCmd; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &SBase::OnDump; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &SBase::OnDump; - (*FnMapx[0])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &SBase::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NM )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ID )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ALL )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NGRP)] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::IGRP)] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NSUP)] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ISUP)] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::SUPV )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::EXTN )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::NM )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::IN )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::OUT )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::ATTR )] = &SBase::OnSend; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NM )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ID )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ALL )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NGRP )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::IGRP )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NSUP )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ISUP )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::SUPV )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::EXTN )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::NM )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::IN )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::OUT )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::ATTR )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &SBase::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &SBase::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &SBase::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &SBase::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &SBase::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &SBase::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::LIST )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK,Q::TNF )] = &SBase::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::TASK )] = &SBase::OnData; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::DEVT )] = &SBase::OnData; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::DEVI )] = &SBase::OnData; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::DEVE )] = &SBase::OnData; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::EXTN )] = &SBase::OnData; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::SUPV )] = &SBase::OnData; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::ON )] = &SBase::OnCmd; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::OFF )] = &SBase::OnCmd; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::ON )] = &SBase::OnCmd; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::OFF )] = &SBase::OnCmd; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &SBase::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &SBase::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &SBase::OnDump; } SBase::~SBase() { + WALKVECTOR(FnMap_t*,FnMapx,F) // WALKVECTOR and WALKMAP are in macros.h (long include chain) + delete *F; // get rid of derived class function tables } unsigned SBase::OnCfg(PMsg_p *msg, unsigned comm) @@ -133,7 +135,7 @@ unsigned SBase::OnData(PMsg_p *msg, unsigned comm) unsigned SBase::OnDump(PMsg_p *msg, unsigned comm) { - if (msg->L(2) == Q::LIST) return DumpSummary(msg); + if (msg->L(2) == Q::LIST) return DumpSummary(msg, comm); if (msg->L(2) != Q::TASK) { Post(700,uint2str(msg->Key()),int2str(Urank)); @@ -142,9 +144,9 @@ unsigned SBase::OnDump(PMsg_p *msg, unsigned comm) switch(msg->L(3)) { case Q::ALL: - return DumpAll(msg); + return DumpAll(msg, comm); case Q::NM: - return DumpTask(msg); + return DumpTask(msg, comm); default: Post(700,uint2str(msg->Key()),int2str(Urank)); return 0; @@ -235,67 +237,68 @@ unsigned SBase::Connect(string svc) { unsigned connErr = MPI_SUCCESS; if ((connErr = CommonBase::Connect(svc)) != MPI_SUCCESS) return connErr; + FnMapx.push_back(new FnMap_t); // add another function table in the nameserver base class int fIdx=FnMapx.size()-1; // Load event handler map for the new comm - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NM )] = &SBase::OnSend; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ID )] = &SBase::OnSend; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ALL )] = &SBase::OnSend; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NGRP)] = &SBase::OnSend; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::IGRP)] = &SBase::OnSend; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NSUP)] = &SBase::OnSend; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ISUP)] = &SBase::OnSend; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::SUPV )] = &SBase::OnSend; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::EXTN )] = &SBase::OnSend; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::NM )] = &SBase::OnSend; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::IN )] = &SBase::OnSend; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::OUT )] = &SBase::OnSend; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::SEND,Q::ATTR )] = &SBase::OnSend; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NM )] = &SBase::OnQuery; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ID )] = &SBase::OnQuery; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ALL )] = &SBase::OnQuery; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NGRP )] = &SBase::OnQuery; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::IGRP )] = &SBase::OnQuery; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NSUP )] = &SBase::OnQuery; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ISUP )] = &SBase::OnQuery; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::SUPV )] = &SBase::OnQuery; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::EXTN )] = &SBase::OnQuery; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::NM )] = &SBase::OnQuery; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::IN )] = &SBase::OnQuery; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::OUT )] = &SBase::OnQuery; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::QRY,Q::ATTR )] = &SBase::OnQuery; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &SBase::OnCfg; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &SBase::OnCfg; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &SBase::OnCfg; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &SBase::OnCfg; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &SBase::OnCfg; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &SBase::OnCfg; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NF )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::TNF )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::TNF )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::TNF )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::TNF )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::TNF )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::LIST )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::TASK )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::RPLY,Q::TASK,Q::TNF )] = &SBase::OnReply; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DATA,Q::TASK )] = &SBase::OnData; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DATA,Q::DEVT )] = &SBase::OnData; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DATA,Q::DEVI )] = &SBase::OnData; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DATA,Q::DEVE )] = &SBase::OnData; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DATA,Q::EXTN )] = &SBase::OnData; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DATA,Q::SUPV )] = &SBase::OnData; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::ON )] = &SBase::OnCmd; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::OFF )] = &SBase::OnCmd; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::ON )] = &SBase::OnCmd; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::OFF )] = &SBase::OnCmd; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &SBase::OnDump; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &SBase::OnDump; - (*FnMapx[fIdx])[Msg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &SBase::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NM )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ID )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ALL )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NGRP)] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::IGRP)] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NSUP)] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ISUP)] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::SUPV )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::EXTN )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::NM )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::IN )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::OUT )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::ATTR )] = &SBase::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NM )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ID )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ALL )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NGRP )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::IGRP )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NSUP )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ISUP )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::SUPV )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::EXTN )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::NM )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::IN )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::OUT )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::ATTR )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &SBase::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &SBase::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &SBase::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &SBase::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &SBase::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &SBase::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::LIST )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK,Q::TNF )] = &SBase::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::TASK )] = &SBase::OnData; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::DEVT )] = &SBase::OnData; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::DEVI )] = &SBase::OnData; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::DEVE )] = &SBase::OnData; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::EXTN )] = &SBase::OnData; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DATA,Q::SUPV )] = &SBase::OnData; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::ON )] = &SBase::OnCmd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::MONI,Q::OFF )] = &SBase::OnCmd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::ON )] = &SBase::OnCmd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CMDC,Q::LOGN,Q::OFF )] = &SBase::OnCmd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &SBase::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &SBase::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &SBase::OnDump; return MPI_SUCCESS; } @@ -363,7 +366,13 @@ unsigned SBase::ConfigDelete(PMsg_p *msg, unsigned comm) unsigned SBase::ConfigDistribute(PMsg_p *msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + if (TaskState(taskName,TaskState_t(Deployed))) + { + Post(724,taskName,int2str(Urank)); + return ERR_TASK_NOT_FOUND; + } + return SUCCESS; } unsigned SBase::ConfigDir(PMsg_p *msg, unsigned comm) @@ -500,7 +509,7 @@ unsigned SBase::DataDevice(PMsg_p *msg) msg->Get(1, devAddrs); if (devNames.size() != devAddrs.size()) { - Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAddrs.size()),"address"); + Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAddrs.size()),"addresse"); return ERR_DEVICE_DATA_MISMATCH; } msg->Get(2, devAttrs); @@ -690,7 +699,7 @@ unsigned SBase::DataSupervisor(PMsg_p *msg) // ----------------------------------------------------------------------------- -unsigned SBase::DumpAll(PMsg_p *msg) +unsigned SBase::DumpAll(PMsg_p *msg, unsigned comm) { // filename must be treated as an absolute literal, because tasks could in // general have different paths, therefore there is no way to infer what @@ -711,7 +720,7 @@ unsigned SBase::DumpAll(PMsg_p *msg) return err; } -unsigned SBase::DumpSummary(PMsg_p *msg) +unsigned SBase::DumpSummary(PMsg_p *msg, unsigned comm) { string filename = msg->Zname(1); FILE* dumpFile; @@ -722,7 +731,7 @@ unsigned SBase::DumpSummary(PMsg_p *msg) return AddressBook::SUCCESS; } -unsigned SBase::DumpTask(PMsg_p *msg) +unsigned SBase::DumpTask(PMsg_p *msg, unsigned comm) { string taskName = msg->Zname(0); if (taskName.empty()) diff --git a/Source/NameServer/SBase.h b/Source/NameServer/SBase.h index 7cc82b21..de2fee35 100644 --- a/Source/NameServer/SBase.h +++ b/Source/NameServer/SBase.h @@ -14,7 +14,7 @@ class SBase : public CommonBase, public AddressBook SBase(int,char **,string,string); virtual ~ SBase(); -inline void Dump(FILE * f = stdout, string s = "") {AddressBook::Dump(f, s);}; +inline virtual void Dump(FILE * f = stdout, string s = "") {AddressBook::Dump(f, s);}; unsigned OnCfg (PMsg_p *, unsigned); unsigned OnCmd (PMsg_p *, unsigned); @@ -34,6 +34,9 @@ virtual unsigned ConfigDir (PMsg_p *, unsigned); virtual unsigned ConfigDistribute (PMsg_p *, unsigned); virtual unsigned ConfigRecall (PMsg_p *, unsigned); virtual unsigned ConfigState (PMsg_p *, unsigned); +virtual unsigned DumpAll (PMsg_p *, unsigned); +virtual unsigned DumpSummary (PMsg_p *, unsigned); +virtual unsigned DumpTask (PMsg_p *, unsigned); virtual unsigned QueryDevIAll (PMsg_p *, unsigned); virtual unsigned QueryDevIByName (PMsg_p *, unsigned); virtual unsigned QueryDevIByID (PMsg_p *, unsigned); @@ -59,9 +62,6 @@ unsigned DataDevice (PMsg_p *); unsigned DataDeviceExternal (PMsg_p *); unsigned DataExternal (PMsg_p *); unsigned DataSupervisor (PMsg_p *); -unsigned DumpAll (PMsg_p *); -unsigned DumpSummary (PMsg_p *); -unsigned DumpTask (PMsg_p *); unsigned QueryAttr (PMsg_p *, unsigned); unsigned QueryDevT (PMsg_p *, unsigned); unsigned QueryExtn (PMsg_p *, unsigned); diff --git a/Source/OrchBase/CMsg_p.cpp b/Source/OrchBase/CMsg_p.cpp index 26af69db..04d51023 100644 --- a/Source/OrchBase/CMsg_p.cpp +++ b/Source/OrchBase/CMsg_p.cpp @@ -6,6 +6,21 @@ CMsg_p::CMsg_p(CMsg_p & r):PMsg_p(r){} CMsg_p::CMsg_p(PMsg_p & r):PMsg_p(r){} CMsg_p::~CMsg_p(void){} +void CMsg_p::Dump(FILE * fp) +{ + fprintf(fp,"--------------------------------------------------------------------------------\n"); + fprintf(fp," Distribution Message Dump\n\n"); + fprintf(fp,"Task: %s\n",Zname(0).c_str()); + fprintf(fp,"Binary path: %s\n\n",Zname(1).c_str()); + fprintf(fp,"---------------- Core Map -----------------\n"); + fprintf(fp,"Virtual core number Physical core\n\n"); + vector > coreMap; + Get(coreMap); + for (vector >::iterator core = coreMap.begin(); core != coreMap.end(); core++) + fprintf(fp, "%19d,Box:%d, Board:%d, Mailbox:%d, Core:%d\n",core->first,core->second.A_box,core->second.A_board,core->second.A_mailbox,core->second.A_core); + fprintf(fp,"________________________________________________________________________________\n"); + fflush(fp); +} template <> void CMsg_p::Dump >(FILE * fp) { @@ -18,10 +33,10 @@ pair * CMsg_p::Get(int & cnt) unsigned* pCIdx = PMsg_p::Get(1,numCores[0]); unsigned* pBox = PMsg_p::Get(2,numCores[1]); unsigned* pBoard = PMsg_p::Get(3,numCores[2]); - unsigned* pMailbox = PMsg_p::Get(3,numCores[3]); - unsigned* pCore = PMsg_p::Get(4,numCores[4]); - unsigned* pThread = PMsg_p::Get(5,numCores[5]); - unsigned* pDevice = PMsg_p::Get(6,numCores[6]); + unsigned* pMailbox = PMsg_p::Get(4,numCores[3]); + unsigned* pCore = PMsg_p::Get(5,numCores[4]); + unsigned* pThread = PMsg_p::Get(6,numCores[5]); + unsigned* pDevice = PMsg_p::Get(7,numCores[6]); if ((numCores[6] | numCores[5] | numCores[4] | numCores[3] | numCores[2] | numCores[1]) != numCores[0]) return 0; if (!pCIdx || !pBox || !pBoard || !pMailbox || !pCore || !pThread || !pDevice) return 0; cnt = numCores[0]; diff --git a/Source/OrchBase/CMsg_p.h b/Source/OrchBase/CMsg_p.h index 3d8eb7b2..8935b2e4 100644 --- a/Source/OrchBase/CMsg_p.h +++ b/Source/OrchBase/CMsg_p.h @@ -14,17 +14,18 @@ class CMsg_p : public PMsg_p virtual ~CMsg_p(); + void Dump(FILE * = stdout); template void Dump(FILE * = stdout); - template inline T * Get(int k,int & cnt) {return (T*)0;}; + template inline T * Get(int k,int & cnt) {return k ? (T*)0 : PMsg_p::Get(k,cnt);}; pair * Get(int &); - inline void Get(int k,string & s) {if (!k) PMsg_p::Get(k,s);}; + inline void Get(int k,string & s) {return;}; template inline void Get(int k,vector & vT) {return;}; void Get(vector > &); inline void GetX(int k,vector & vs) {return;}; template inline int Put() {return -1;}; - template void Put(int k,T * data,int cnt=1) {return;}; + template void Put(int k,T * data,int cnt=1) {if (!k) PMsg_p::Put(k,data,cnt);}; void Put(pair *,int=1); - inline void Put(int k,string * data) {if (!k) PMsg_p::Put(k,data);}; + inline void Put(int k,string * data) {return;}; template inline void Put(int k,vector * data) {return;}; void Put(vector > *); inline void PutX(int k,vector * data) {return;}; diff --git a/Source/OrchBase/OrchBaseLink.cpp b/Source/OrchBase/OrchBaseLink.cpp index 9151aa04..9d976abd 100644 --- a/Source/OrchBase/OrchBaseLink.cpp +++ b/Source/OrchBase/OrchBaseLink.cpp @@ -263,7 +263,7 @@ if (!pPlace->Place(pT)) vector devAttrs; // vector devices; // RecordData_t deviceRecord; - DTypeIdx devType = 0; + // DTypeIdx devType = 0; // deviceRecord.Supervisor = supervisorRecord.Address; // deviceRecord.RecordType = Device; // deviceRecord.DeviceType = devType; diff --git a/Source/OrchBase/OrchBaseTask.cpp b/Source/OrchBase/OrchBaseTask.cpp index 49ff9c2e..15a9dc9a 100644 --- a/Source/OrchBase/OrchBaseTask.cpp +++ b/Source/OrchBase/OrchBaseTask.cpp @@ -578,17 +578,14 @@ vector > coreVec; // core map container to send vector::iterator currBox = pPmap[cIdx]->vPmap.begin(); // process map for the Mothership being deployed to -CMsg_p PktC; -PMsg_p PktD; -//PMsg_p PktC, PktD; // messages to send to each participating box -PktC.Key(Q::NAME,Q::DIST); // distributing the core mappings -PktD.Key(Q::NAME,Q::TDIR); // and the data directory +CMsg_p PktC; // message to send to each participating box +PktC.Key(Q::NAME,Q::CFG,Q::DIST); // distributing the core mappings and the data directory PktC.Src(Urank); string taskname = task->first; -PktC.Put(0, &taskname); // first field in the packet is the task name -PktD.Put(0, &taskname); -taskname+="/"; // for the moment the binary directory will be fixed -taskname+=BIN_PATH; // later we could make this user-settable. +PktC.Zname(0, taskname); // first string field in the packet is the task name +//PktC.Put(0, &taskname); // first field in the packet is the task name +taskname+="/"; // for the moment the binary directory will be fixed +taskname+=BIN_PATH; // later we could make this user-settable. // duplicate P_builder's iteration through the task P_core* thisCore; // Core available during iteration. P_thread* firstThread; // The "first" thread in thisCore. "first" is arbitrary, because cores are stored in a map. @@ -601,7 +598,7 @@ while (cIdx < Comms.size()) // grab the next available mothership ++cIdx; } taskname.insert(0,string("/home/")+currBox->P_user+"/"); -PktD.Put(1,&taskname); +PktC.Zname(1,taskname); // second string field is the task binary directory coreVec.clear(); // reset the packet content WALKVECTOR(P_board*,boxNode->second->P_boardv,board) { @@ -674,16 +671,26 @@ else system((string("ssh ")+currBox->P_user+"@"+currBox->P_proc+ "\"mkdir "+task->first+"\"").c_str()); system((string("scp -r ")+taskpath+task->first+"/"+BIN_PATH+" "+currBox->P_user+"@"+currBox->P_proc+":"+taskname).c_str()); } -PktD.comm = PktC.comm = Comms[cIdx]; // Packet will go on the communicator it was found on -printf("Sending a distribution message to mothership with %lu cores\n", coreVec.size()); -PktC.Put(&coreVec); // place the core map in the packet to this Mothership -/* -printf("Sending a single-core distribution message to mothership cores\n"); -PktC.Put(1,&(coreVec[0].first),1); -PktC.Put(2,&(coreVec[0].second),1); -*/ -PktC.Send(currBox->P_rank); // Send to the target Mothership -PktD.Send(currBox->P_rank); +PktC.comm = Comms[cIdx]; // Packet will go on the communicator it was found on +Post(726,int2str(currBox->P_rank),uint2str(coreVec.size())); +PktC.Put(0,&currBox->P_rank); // first field is the Mothership's address (rank or symbolic address) +PktC.Put(&coreVec); // place the core map in the packet to this Mothership +// PktC.Send(currBox->P_rank); // Send to the target Mothership +unsigned nsComm = 0; +for (; nsComm < pPmap.size(); nsComm++) // find the NameServer +{ + if (pPmap[nsComm]->U.NameServer != Q::NAP) + { + PktC.comm = Comms[nsComm]; // send the distribution message + PktC.Send(pPmap[nsComm]->U.NameServer); // to the NameServer + break; + } +} +if (nsComm >= pPmap.size()) // No NameServer. A severe error. +{ + Post(711); + return; +} } // Next Mothership } diff --git a/Source/OrchestratorMessages.txt b/Source/OrchestratorMessages.txt index 93c64303..5990a1cc 100644 --- a/Source/OrchestratorMessages.txt +++ b/Source/OrchestratorMessages.txt @@ -123,6 +123,11 @@ 723(W) : "Task binary directory path to SBase at rank %s is empty. Current working directory will be used" 724(E) : "No task %s on SBase at rank %s" 725(E) : "Error attempting to set state for task %s on SBase at rank %s" +726(I) : "Deploying data to Mothership at rank %s: %s cores distributed" +727(W) : "SBase map on rank %s is invalid. Rebuild before performing tasks on this SBase" +728(E) : "Error: SBase at rank %s has a record for Supervisor rank %s, but no such Supervisor exists" +729(E) : "Supervisor %s registered as a device for Supervisor %s, but only one Supervisor can exist" +730(W) : "Invalid command %s to SBase rank %s: missing value %s" 801(D) : "P_builder::Add(name=%s,file=%s)" 802(D) : "Removing task %s from the Orchestrator" From 7c61b0dd7b1baf539c3b6e73bec847d0236a0076 Mon Sep 17 00:00:00 2001 From: AlexRast Date: Fri, 16 Aug 2019 03:04:43 +0100 Subject: [PATCH 04/14] added support for queries and responses in SBase and dummy Mothership --- Source/Mothership/MothershipDummy.cpp | 687 ++++++++++++++++++++++++-- Source/Mothership/MothershipDummy.h | 18 +- Source/NameServer/AddressBook.cpp | 1 + Source/NameServer/AddressBook.hpp | 3 + Source/NameServer/NameServer.cpp | 8 +- Source/NameServer/SBase.cpp | 573 ++++++++++++++++++--- Source/NameServer/SBase.h | 3 + Source/OrchestratorMessages.txt | 11 +- 8 files changed, 1167 insertions(+), 137 deletions(-) diff --git a/Source/Mothership/MothershipDummy.cpp b/Source/Mothership/MothershipDummy.cpp index e733fba6..8a41c23d 100644 --- a/Source/Mothership/MothershipDummy.cpp +++ b/Source/Mothership/MothershipDummy.cpp @@ -2,40 +2,68 @@ #include "CMsg_p.h" #include "Pglobals.h" #include +#include MothershipDummy::MothershipDummy(int argc,char * argv[], string d) : SBase(argc,argv,d,string(__FILE__)) { - FnMapx.push_back(new FnMap_t); // create a default function table - // Load the message map + FnMapx.push_back(new FnMap_t); // create a default function table + // Load the message map - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &MothershipDummy::OnCfg; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &MothershipDummy::OnCfg; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &MothershipDummy::OnCfg; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &MothershipDummy::OnCfg; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &MothershipDummy::OnCfg; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &MothershipDummy::OnCfg; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL)] = &MothershipDummy::OnDump; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &MothershipDummy::OnDump; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &MothershipDummy::OnDump; - (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &MothershipDummy::OnCmnd; - (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &MothershipDummy::OnCmnd; - (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &MothershipDummy::OnCmnd; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DIST )] = &MothershipDummy::OnName; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RECL )] = &MothershipDummy::OnName; - (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::TDIR )] = &MothershipDummy::OnName; - (*FnMapx[0])[PMsg_p::KEY(Q::SUPR )] = &MothershipDummy::OnSuper; - (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &MothershipDummy::OnSyst; - (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &MothershipDummy::OnSyst; - (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &MothershipDummy::OnSyst; - (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &MothershipDummy::OnSyst; - - MPISpinner(); // Spin on MPI messages; exit only on DIE + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &MothershipDummy::OnCfg; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &MothershipDummy::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &MothershipDummy::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &MothershipDummy::OnDump; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NM )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::ID )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::ALL )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NGRP)] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::IGRP)] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NSUP)] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::ISUP)] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::NM )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::IN )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::OUT )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::LIST )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::LIST,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &MothershipDummy::OnCmnd; + (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &MothershipDummy::OnCmnd; + (*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &MothershipDummy::OnCmnd; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DIST )] = &MothershipDummy::OnName; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RECL )] = &MothershipDummy::OnName; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::TDIR )] = &MothershipDummy::OnName; + (*FnMapx[0])[PMsg_p::KEY(Q::SUPR )] = &MothershipDummy::OnSuper; + (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &MothershipDummy::OnSyst; + (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &MothershipDummy::OnSyst; + (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &MothershipDummy::OnSyst; + (*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &MothershipDummy::OnSyst; + + MPISpinner(); // Spin on MPI messages; exit only on DIE } MothershipDummy::~MothershipDummy() -{ +{ WALKVECTOR(FnMap_t*,FnMapx,F) // WALKVECTOR and WALKMAP are in macros.h (long include chain) delete *F; // get rid of derived class function tables } @@ -47,26 +75,53 @@ unsigned MothershipDummy::Connect(string svc) if ((connErr = SBase::Connect(svc)) != MPI_SUCCESS) return connErr; FnMapx.push_back(new FnMap_t); // add another function table in the derived class int fIdx=FnMapx.size()-1; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &MothershipDummy::OnCfg; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &MothershipDummy::OnCfg; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &MothershipDummy::OnCfg; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &MothershipDummy::OnCfg; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &MothershipDummy::OnCfg; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &MothershipDummy::OnCfg; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL)] = &MothershipDummy::OnDump; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &MothershipDummy::OnDump; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &MothershipDummy::OnDump; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &MothershipDummy::OnCmnd; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &MothershipDummy::OnCmnd; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &MothershipDummy::OnCmnd; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DIST )] = &MothershipDummy::OnName; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RECL )] = &MothershipDummy::OnName; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::TDIR )] = &MothershipDummy::OnName; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::SUPR )] = &MothershipDummy::OnSuper; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &MothershipDummy::OnSyst; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &MothershipDummy::OnSyst; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &MothershipDummy::OnSyst; - (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &MothershipDummy::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &MothershipDummy::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &MothershipDummy::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &MothershipDummy::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &MothershipDummy::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NM )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::ID )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::ALL )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NGRP)] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::IGRP)] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NSUP)] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::ISUP)] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVI,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::SUPV,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::EXTN,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::NM )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::IN )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::OUT )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::DEVT,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::NF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::ATTR,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::LIST )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::LIST,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RPLY,Q::TASK,Q::TNF )] = &MothershipDummy::OnReply; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &MothershipDummy::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &MothershipDummy::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &MothershipDummy::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DIST )] = &MothershipDummy::OnName; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::RECL )] = &MothershipDummy::OnName; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::TDIR )] = &MothershipDummy::OnName; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SUPR )] = &MothershipDummy::OnSuper; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &MothershipDummy::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &MothershipDummy::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &MothershipDummy::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &MothershipDummy::OnSyst; return MPI_SUCCESS; } @@ -109,6 +164,23 @@ unsigned MothershipDummy::OnCfg(PMsg_p *msg, unsigned comm) unsigned MothershipDummy::OnCmnd(PMsg_p *msg, unsigned comm) { + // co-opt the task /init (which sends LOAD) command to trigger some + // test queries. + if (msg->L(1) == Q::LOAD) + { + PMsg_p dummyState(Comms[0]); + string taskName; + msg->Get(0,taskName); + if (taskName.empty()) + { + Post(510,"Load",int2str(Urank)); + return 0; + } + dummyState.Zname(0,taskName); + dummyState.Zname(1,"Init"); + return ConfigState(&dummyState, 0); + } + // any other command is silently ignored. return 0; } @@ -137,6 +209,43 @@ unsigned MothershipDummy::OnName(PMsg_p *msg, unsigned comm) return 0; } +unsigned MothershipDummy::OnReply(PMsg_p *msg, unsigned comm) +{ + if ((msg->L(3) == Q::NF) || (msg->L(3) == Q::TNF)) return ReplyNotFound(msg,comm); + switch(msg->L(2)) + { + case Q::DEVI: + switch(msg->L(3)) + { + case Q::NM: + case Q::ID: + return ReplyDevice(msg,comm); + case Q::NSUP: + case Q::ISUP: + return ReplyDevSuper(msg,comm); + case Q::ALL: + case Q::NGRP: + case Q::IGRP: + return ReplyDevices(msg,comm); + } + break; + case Q::SUPV: + return ReplySupers(msg,comm); + case Q::EXTN: + return ReplyDevices(msg,comm); + case Q::DEVT: + return ReplyDevTypes(msg,comm); + case Q::ATTR: + return ReplyAttrs(msg,comm); + case Q::LIST: + return ReplyList(msg,comm); + case Q::TASK: + return ReplyTask(msg,comm); + } + Post(700,uint2str(msg->Key()),int2str(Urank)); + return ERR_NONFATAL; +} + unsigned MothershipDummy::OnSuper(PMsg_p *msg, unsigned comm) { return 0; @@ -187,8 +296,8 @@ void MothershipDummy::Dump(FILE *fp, string task) unsigned MothershipDummy::ConfigDistribute(PMsg_p *msg, unsigned comm) { unsigned err = SUCCESS; - if (err = SBase::ConfigDir(msg,comm)) return err; - if (err = SBase::ConfigDistribute(msg,comm)) return err; + if ((err = SBase::ConfigDir(msg,comm))) return err; + if ((err = SBase::ConfigDistribute(msg,comm))) return err; string taskName = msg->Zname(0); // temporarily we dump everything out to a file --------------------------------------------- FILE *dumpFile = fopen("MothershipDummy_dump.txt","w"); @@ -203,6 +312,12 @@ unsigned MothershipDummy::ConfigDistribute(PMsg_p *msg, unsigned comm) Dump(dumpFile,taskName); fclose(dumpFile); //-------------------------------------------------------------------------------------------- + PMsg_p stateSet(Comms[0]); // set up state directly + stateSet.Src(Urank); + stateSet.Tgt(Urank); + stateSet.Zname(0, taskName); + stateSet.Zname(1, "Deployed"); // by creating a message to ourselves + ConfigState(&stateSet, 0); // and calling the state-transition function with it return err; } @@ -213,7 +328,7 @@ unsigned MothershipDummy::ConfigRecall(PMsg_p *msg, unsigned comm) unsigned err = SUCCESS; if (taskName.empty()) { - if (err = ListTask(tasks)) + if ((err = ListTask(tasks))) { Post(721,"gett","list",int2str(Urank)); return err; @@ -223,7 +338,7 @@ unsigned MothershipDummy::ConfigRecall(PMsg_p *msg, unsigned comm) vector::iterator task; for (task = tasks.begin(); task != tasks.end(); task++) { - if (err = DelTask(*task)) + if ((err = DelTask(*task))) { Post(721,"delet",*task,int2str(Urank)); err = ERR_NONFATAL; @@ -232,9 +347,138 @@ unsigned MothershipDummy::ConfigRecall(PMsg_p *msg, unsigned comm) return err; } +// for testing purposes we will coopt ConfigState on Dummy motherships +// to query the NameServer for data. unsigned MothershipDummy::ConfigState(PMsg_p *msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + TaskData_t task; + unsigned err = GetTask(taskName, task); + if (err) return SUCCESS; // No such task yet. We can ignore. + if (task.State == Deployed) + { + string nextState = msg->Zname(1); + const vector* deployedDevs; + if (nextState == "Init") // Init state change triggers queries + { + err = GetDevices(taskName, deployedDevs); // but only if devices are loaded + if (!err) + { + // pick a subsample of 10 devices to run single queries on (or, at least, + // as many devices as we have if it's < 10) + unsigned devStep = deployedDevs->size()/10 == 0 ? 1 : deployedDevs->size()/10; + PMsg_p qry; + // find the NameServer's rank and comm + for (unsigned comm = 0; comm < pPmap.size(); comm++) + { + if (pPmap[comm]->U.NameServer != Q::NAP) // use the first match + { + qry.comm = Comms[comm]; + qry.Src(Urank); + qry.Tgt(pPmap[comm]->U.NameServer); + break; + } + } + if (comm == pPmap.size()) // no NameServer found. A severe error. + { + Post(711); + return ERR_INVALID_STATE; + } + // now send some dummy queries. Get a task list first. + qry.Key(Q::NAME,Q::QRY,Q::LIST); + // be sure to set up tags for each query sent so it can be matched + int tag = 1; // stay out of standard Orchestrator-land (this is an MPI tag) + if (qryMap.size()) tag = qryMap.rbegin()->first + 1; + if (tag > MPI_TAG_UB) tag = 1; + qry.Tag(tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(2,Q::TASK); // now ask about the specific task we are on + qry.Zname(0,taskName); + qry.Tag(++tag); // these 3 lines are standard boilerplate to send + qryMap[tag]=qry.Key(); // a query and update the tag map. + qry.Send(); + qry.L(2,Q::DEVI); + // try all the device queries for a sample of devices (max 10) + for (unsigned dev = 0; dev < deployedDevs->size(); dev+=devStep) + { + qry.L(3,Q::NM); // by name + qry.Zname(1,(*deployedDevs)[dev].Name); + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::NGRP); // in same group as named device + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::NSUP); // supervisor of named device + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::ID); // by ID + qry.Zname(1,""); + // another case where we have to copy into a temporary because + // the device records are const-qualified in the return from + // GetDevices. + SymAddr_t devAddr = (*deployedDevs)[dev].Address; + qry.Put(0,&devAddr); + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::IGRP); // in same group as device ID + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::ISUP); // supervisor of device ID + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + } + qry.L(3,Q::ALL); // all devices + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,0); + qry.L(2,Q::SUPV); // all supervisors + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(2,Q::EXTN); // all externals (should be none for now) + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + if (task.AttributeTypes.size()) + { + qry.L(2,Q::ATTR); // matching attributes, if any exist + qry.PutX(2,&(task.AttributeTypes)); // just use all attributes + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + } + if (!task.DeviceTypes.size()) // device types *should* exist, + { + // but if there are none send us a message and exit. + Post(706,taskName,int2str(Urank)); + return ERR_NONFATAL; + } + qry.L(2,Q::DEVT); // of same device type + qry.L(3,Q::NM); + qry.Zname(2,task.DeviceTypes.front().Name); + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::IN); // sharing inputs with device type + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + qry.L(3,Q::OUT); // sharing outputs with device type + qry.Tag(++tag); + qryMap[tag]=qry.Key(); + qry.Send(); + } // if (!err) + } // if (nextState == "Init") + } // if (task.State == Deployed) + return SBase::ConfigState(msg,comm); // (always) update state through base class } unsigned MothershipDummy::DumpAll(PMsg_p * msg, unsigned comm) @@ -251,3 +495,346 @@ unsigned MothershipDummy::DumpTask(PMsg_p * msg, unsigned comm) { return SBase::DumpTask(msg, comm); } + +void MothershipDummy::MapValue2Key(unsigned value, vector* key) +{ + uint32_t val = static_cast(value); + void* valPtr = static_cast(&val); + for (unsigned b=0; b < sizeof(uint32_t); b++) + key->push_back(static_cast(valPtr)[b]); +} + +string MothershipDummy::QueryType(unsigned keyVal) +{ + vector key; + MapValue2Key(keyVal, &key); + if ((key[0] != Q::NAME) || (key[1] != Q::QRY)) return "Not a Query"; + switch (key[2]) + { + case Q::DEVI: + switch (key[3]) + { + case Q::NM: + return "Device By Name"; + case Q::ID: + return "Device By ID"; + case Q::ALL: + return "All Devices"; + case Q::NGRP: + return "Devices in Same Group as Named Device"; + case Q::IGRP: + return "Devices in Same Group as Device ID"; + case Q::NSUP: + return "Supervisor of Named Device"; + case Q::ISUP: + return "Supervisor of Device ID"; + default: + return "Unrecognised Device Query"; + } + case Q::SUPV: + return "Supervisors"; + case Q::EXTN: + return "External Devices"; + case Q::DEVT: + switch (key[3]) + { + case Q::NM: + return "Devices of Type"; + case Q::IN: + return "Devices With Input Message Type"; + case Q::OUT: + return "Devices With Output Message Type"; + default: + return "Unrecognised Type Query"; + } + case Q::ATTR: + return "Devices by Attribute"; + case Q::LIST: + return "Task List"; + case Q::TASK: + return "Task Info"; + default: + return "Unrecognised Query"; + } +} + +unsigned MothershipDummy::ReplyAttrs(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + vector devAddrs; + vector devNames; + vector devAttrs; + msg->Get(0, devAddrs); + msg->GetX(1, devNames); + msg->GetX(2, devAttrs); + unsigned numDevs = devAddrs.size(); + unsigned numNames = devNames.size(); + if (numDevs != numNames) + { + Post(716, int2str(Urank), uint2str(numNames), uint2str(numDevs), "addresse"); + numDevs = (numDevs > numNames) ? numNames : numDevs; + } + fprintf(dumpFile, "--------------------- Devices By Attribute -------------------------------------\n"); + for (unsigned dev = 0; dev < numDevs; dev++) + fprintf(dumpFile, "Device %s: address 0x%llx\n", devNames[dev].c_str(), devAddrs[dev]); + fprintf(dumpFile, "\n"); + fprintf(dumpFile, "Matched attributes:\n"); + for (unsigned attr = 0; attr < devAttrs.size(); attr++) + fprintf(dumpFile, "%s\n", devAttrs[attr].c_str()); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyDevice(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + int numDevs; + SymAddr_t* devAddr = msg->Get(0, numDevs); + if (!devAddr) + { + fprintf(dumpFile, "ERROR: device address missing for device %s query\n", msg->Zname(1).c_str()); + return ERR_NONFATAL; + } + string qryType = QueryType(msg->Key()); + fprintf(dumpFile, "--------------------- %s ----------------------------\n", qryType.c_str()); + fprintf(dumpFile, "Device %s: address 0x%llx\n", msg->Zname(1).c_str(), *devAddr); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyDevices(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + vector devAddrs; + vector devNames; + msg->Get(0, devAddrs); + msg->GetX(1, devNames); + unsigned numDevs = devAddrs.size(); + unsigned numNames = devNames.size(); + if (numDevs != numNames) + { + Post(716, int2str(Urank), uint2str(numNames), uint2str(numDevs), "addresse"); + numDevs = (numDevs > numNames) ? numNames : numDevs; + } + string devName = msg->Zname(1); + if (msg->L(2) == Q::EXTN) devName = "{Externals}"; + else if (msg->L(3) == Q::ALL) devName = "{All}"; + string qryType = QueryType(msg->Key()); + fprintf(dumpFile, "----------- %s ----------------------------\n", qryType.c_str()); + fprintf(dumpFile, "Matching device %s\n", devName.c_str()); + for (unsigned dev = 0; dev < numDevs; dev++) + fprintf(dumpFile, "Device %s: address 0x%llx\n", devNames[dev].c_str(), devAddrs[dev]); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyDevSuper(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + int numDevs; + SymAddr_t* devAddr = msg->Get(0, numDevs); + SymAddr_t* superAddr = msg->Get(1, numDevs); + if (!devAddr) + { + fprintf(dumpFile, "ERROR: device address missing for device %s supervisor query\n", msg->Zname(1).c_str()); + return ERR_NONFATAL; + } + if (!superAddr) + { + fprintf(dumpFile, "ERROR: device address missing for device %s supervisor query\n", msg->Zname(1).c_str()); + return ERR_NONFATAL; + } + string qryType = QueryType(msg->Key()); + fprintf(dumpFile, "--------------------- %s ----------------------------\n", qryType.c_str()); + fprintf(dumpFile, "Device %s: address 0x%llx, supervisor 0x%llx\n", msg->Zname(1).c_str(),*devAddr,*superAddr); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyDevTypes(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + vector devAddrs; + vector devNames; + msg->Get(0, devAddrs); + msg->GetX(1, devNames); + unsigned numDevs = devAddrs.size(); + unsigned numNames = devNames.size(); + if (numDevs != numNames) + { + Post(716, int2str(Urank), uint2str(numNames), uint2str(numDevs), "addresse"); + numDevs = (numDevs > numNames) ? numNames : numDevs; + } + string matchName = msg->Zname(2); + string matchType = "device"; + if (msg->L(3) == Q::IN) matchType = "input message"; + if (msg->L(3) == Q::OUT) matchType = "output message"; + string qryType = QueryType(msg->Key()); + fprintf(dumpFile, "----------- %s ----------------------------\n", qryType.c_str()); + fprintf(dumpFile, "Matching %s type %s\n", matchType.c_str(), matchName.c_str()); + for (unsigned dev = 0; dev < numDevs; dev++) + fprintf(dumpFile, "Device %s: address 0x%llx\n", devNames[dev].c_str(), devAddrs[dev]); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyList(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + vector tasks; + msg->GetX(1,tasks); + fprintf(dumpFile, "--------------------- Task List -------------------------------------\n"); + WALKVECTOR(string, tasks, task) + fprintf(dumpFile, "%s\n", task->c_str()); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyNotFound(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + fprintf(dumpFile,"Reply to query %s on task %s: %s\n", QueryType(qryIt->second).c_str(), + msg->Zname(0).c_str(), msg->L(3) == Q::NF ? "Device Not Found" : "Task Not Found"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplySupers(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + vector supAddrs; + vector supNames; + vector supRanks; + msg->Get(0, supAddrs); + msg->GetX(1, supNames); + msg->Get(2, supRanks); + unsigned numDevs = supAddrs.size(); + unsigned numNames = supNames.size(); + unsigned numRanks = supRanks.size(); + if ((numDevs != numNames) || (numDevs != numRanks)) + { + Post(737, int2str(Urank), uint2str(numNames), uint2str(numDevs), uint2str(numRanks)); + numDevs = (numDevs > numNames) ? numNames : numDevs; + numDevs = (numDevs > numRanks) ? numRanks : numDevs; + } + fprintf(dumpFile, "--------------------- Supervisors -------------------------------------\n"); + for (unsigned dev = 0; dev < numDevs; dev++) + fprintf(dumpFile, "Supervisor %s: address 0x%llx, rank %d\n",supNames[dev].c_str(),supAddrs[dev],supRanks[dev]); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + +unsigned MothershipDummy::ReplyTask(PMsg_p *msg, unsigned comm) +{ + FILE *dumpFile = fopen("MothershipDummy_dump.txt","a"); + int qryTag = msg->Tag(); + map::iterator qryIt = qryMap.find(qryTag); + if (qryIt == qryMap.end()) + { + fprintf(dumpFile, "ERROR: reply to unknown query %x\n", qryTag); + return ERR_NONFATAL; + } + string task = msg->Zname(0); + string state = msg->Zname(1); + string tdir = msg->Zname(2); + string fname = msg->Zname(3); + vector msgTypes; + vector attrTypes; + vector counts; + msg->GetX(0,msgTypes); + msg->GetX(1, attrTypes); + msg->Get(2, counts); + if (counts.size() != 3) + { + fprintf(dumpFile, "ERROR: count mismatch for task info: expected 3 counts, received %u\n", counts.size()); + return ERR_NONFATAL; + } + fprintf(dumpFile, "-------------------------------- Task Info -------------------------------------\n"); + fprintf(dumpFile, "Task: %s\n", task.c_str()); + fprintf(dumpFile, "In state: %s\n", state.c_str()); + fprintf(dumpFile, "Task directory: %s\n", tdir.c_str()); + fprintf(dumpFile, "Source file name: %s\n", fname.c_str()); + fprintf(dumpFile, "---- Message types ----\n"); + WALKVECTOR(string, msgTypes, msgTyp) + fprintf(dumpFile, "%s\n", msgTyp->c_str()); + fprintf(dumpFile, "_______________________\n"); + WALKVECTOR(string, attrTypes, attrTyp) + fprintf(dumpFile, "%s\n", attrTyp->c_str()); + fprintf(dumpFile, "Number of devices: %lu, externals: %lu, supervisors %lu\n",counts[0],counts[1],counts[2]); + fprintf(dumpFile, "________________________________________________________________________________\n"); + fclose(dumpFile); + qryMap.erase(qryTag); // Reply handled; remove the query request + return SUCCESS; +} + diff --git a/Source/Mothership/MothershipDummy.h b/Source/Mothership/MothershipDummy.h index e09bbbc3..1f8362c0 100644 --- a/Source/Mothership/MothershipDummy.h +++ b/Source/Mothership/MothershipDummy.h @@ -18,10 +18,11 @@ class MothershipDummy : public SBase unsigned CmLoad(string); unsigned CmRun(string); unsigned CmStop(string); - unsigned OnCfg(PMsg_p *, unsigned); + unsigned OnCfg(PMsg_p *,unsigned); unsigned OnCmnd(PMsg_p *,unsigned); - unsigned OnDump(PMsg_p *, unsigned); + unsigned OnDump(PMsg_p *,unsigned); unsigned OnName(PMsg_p *,unsigned); + unsigned OnReply(PMsg_p *, unsigned); unsigned OnSuper(PMsg_p *,unsigned); unsigned OnSyst(PMsg_p *,unsigned); unsigned SystHW(const vector&); @@ -39,11 +40,24 @@ class MothershipDummy : public SBase unsigned DumpAll(PMsg_p *, unsigned); unsigned DumpSummary(PMsg_p *, unsigned); unsigned DumpTask(PMsg_p *, unsigned); + unsigned ReplyAttrs(PMsg_p*, unsigned); + unsigned ReplyDevice(PMsg_p*, unsigned); + unsigned ReplyDevices(PMsg_p*, unsigned); + unsigned ReplyDevSuper(PMsg_p*, unsigned); + unsigned ReplyDevTypes(PMsg_p*, unsigned); + unsigned ReplyList(PMsg_p*, unsigned); + unsigned ReplyNotFound(PMsg_p*, unsigned); + unsigned ReplySupers(PMsg_p*, unsigned); + unsigned ReplyTask(PMsg_p*, unsigned); + + static void MapValue2Key(unsigned value, vector* key); + string QueryType(unsigned); typedef unsigned (MothershipDummy::*pMeth)(PMsg_p *, unsigned); typedef map FnMap_t; vector FnMapx; + map qryMap; }; #endif diff --git a/Source/NameServer/AddressBook.cpp b/Source/NameServer/AddressBook.cpp index c906099c..1f273314 100644 --- a/Source/NameServer/AddressBook.cpp +++ b/Source/NameServer/AddressBook.cpp @@ -28,6 +28,7 @@ const unsigned AddressBook::ERR_TASK_NOT_FOUND; const unsigned AddressBook::ERR_DEVICE_NOT_FOUND; const unsigned AddressBook::ERR_INVALID_MAP; const unsigned AddressBook::ERR_INVALID_SUPERVISOR; +const unsigned AddressBook::ERR_INVALID_STATE; //Constructors AddressBook::AddressBook(std::string d) diff --git a/Source/NameServer/AddressBook.hpp b/Source/NameServer/AddressBook.hpp index f2c60654..78a7d949 100644 --- a/Source/NameServer/AddressBook.hpp +++ b/Source/NameServer/AddressBook.hpp @@ -98,6 +98,8 @@ class AddressBook unsigned FindByInMsg(std::string &TaskName, std::string Msg, const RecordVect_t* &Records); //TODO unsigned FindByOuMsg(std::string &TaskName, std::string Msg, const RecordVect_t* &Records); //TODO + // device getters. Note that a vector is different from a RecordVect_t, + // which is a vector unsigned GetDevices(std::string &TaskName, const std::vector* &Records); unsigned GetExternals(std::string &TaskName, const std::vector* &Records); unsigned GetSupervisors(std::string &TaskName, const std::vector* &Records); @@ -137,6 +139,7 @@ class AddressBook static const unsigned ERR_DEVICE_NOT_FOUND = 11; static const unsigned ERR_INVALID_MAP = 12; static const unsigned ERR_INVALID_SUPERVISOR = 13; + static const unsigned ERR_INVALID_STATE = 14; private: TaskMap_t TaskMap; diff --git a/Source/NameServer/NameServer.cpp b/Source/NameServer/NameServer.cpp index e7313df1..ef6c012d 100644 --- a/Source/NameServer/NameServer.cpp +++ b/Source/NameServer/NameServer.cpp @@ -389,9 +389,15 @@ unsigned NameServer::ConfigRecall(PMsg_p * msg, unsigned comm) return 0; } +/* NameServer only needs set up its own state. Motherships don't + need a state until deployed, because it's only at this point that + name services data is uploaded to them, and after they do have it, + further state changes involve direct explicit commands to the Motherships + anyway. +*/ unsigned NameServer::ConfigState(PMsg_p * msg, unsigned comm) { - return 0; + return SBase::ConfigState(msg, comm); } //============================================================================== diff --git a/Source/NameServer/SBase.cpp b/Source/NameServer/SBase.cpp index 24d8de69..ef1e739a 100644 --- a/Source/NameServer/SBase.cpp +++ b/Source/NameServer/SBase.cpp @@ -1,3 +1,4 @@ + #include "SBase.h" #include "Pglobals.h" #include // debug @@ -26,6 +27,8 @@ SBase::SBase(int argc,char ** argv,string d,string s): CommonBase(argc,argv,d,s) (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::IGRP )] = &SBase::OnQuery; (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NSUP )] = &SBase::OnQuery; (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ISUP )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::LIST )] = &SBase::OnQuery; + (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::TASK )] = &SBase::OnQuery; (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::SUPV )] = &SBase::OnQuery; (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::EXTN )] = &SBase::OnQuery; (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::NM )] = &SBase::OnQuery; @@ -260,6 +263,8 @@ unsigned SBase::Connect(string svc) (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::IGRP )] = &SBase::OnQuery; (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::NSUP )] = &SBase::OnQuery; (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVI,Q::ISUP )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::LIST )] = &SBase::OnQuery; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::TASK )] = &SBase::OnQuery; (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::SUPV )] = &SBase::OnQuery; (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::EXTN )] = &SBase::OnQuery; (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::QRY,Q::DEVT,Q::NM )] = &SBase::OnQuery; @@ -314,7 +319,7 @@ unsigned SBase::ConfigBuild(PMsg_p *msg, unsigned comm) unsigned err = SUCCESS; if (taskName.empty()) { - if (err = ListTask(tasks)) + if ((err = ListTask(tasks))) { Post(721,"rebuild","list",int2str(Urank)); return err; @@ -345,7 +350,7 @@ unsigned SBase::ConfigDelete(PMsg_p *msg, unsigned comm) unsigned err = SUCCESS; if (taskName.empty()) { - if (err = ListTask(tasks)) + if ((err = ListTask(tasks))) { Post(721,"gett","list",int2str(Urank)); return err; @@ -355,7 +360,7 @@ unsigned SBase::ConfigDelete(PMsg_p *msg, unsigned comm) vector::iterator task; for (task = tasks.begin(); task != tasks.end(); task++) { - if (err = DelTask(*task)) + if ((err = DelTask(*task))) { Post(721,"delet",*task,int2str(Urank)); return err; @@ -395,7 +400,7 @@ unsigned SBase::ConfigRecall(PMsg_p *msg, unsigned comm) unsigned err = SUCCESS; if (taskName.empty()) { - if (err = ListTask(tasks)) + if ((err = ListTask(tasks))) { Post(721,"gett","list",int2str(Urank)); return err; @@ -405,12 +410,12 @@ unsigned SBase::ConfigRecall(PMsg_p *msg, unsigned comm) vector::iterator task; for (task = tasks.begin(); task != tasks.end(); task++) { - if (err = ClearTask(*task)) + if ((err = ClearTask(*task))) { Post(721,"clear",*task,int2str(Urank)); return err; } - if (err = TaskState(*task,Unknown)) + if ((err = TaskState(*task,Unknown))) { Post(725,*task,int2str(Urank)); return err; @@ -421,7 +426,27 @@ unsigned SBase::ConfigRecall(PMsg_p *msg, unsigned comm) unsigned SBase::ConfigState(PMsg_p *msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + // State is an enum, which makes it more reliable to send it as a string. + // Otherwise there is no easy way to be certain the value isn't out of range + // (e.g. what if the enum is given non-contiguous values?) + string newStateStr = msg->Zname(1); + TaskState_t newState(Unknown); + // Thus after creating a default unknown state we test each viable possibility + if (newStateStr == "Loaded") newState = Loaded; + else if (newStateStr == "Linked") newState = Linked; + else if (newStateStr == "Built") newState = Built; + else if (newStateStr == "Deployed") newState = Deployed; + else if (newStateStr == "Init") newState = Init; + else if (newStateStr == "Running") newState = Running; + else if (newStateStr == "Finished") newState = Finished; + if (TaskState(taskName,newState)) + { + Post(724,taskName,int2str(Urank)); + return ERR_NONFATAL; // + // return ERR_TASK_NOT_FOUND; + } + return SUCCESS; } unsigned SBase::DataTask(PMsg_p *msg) @@ -491,9 +516,7 @@ unsigned SBase::DataDevice(PMsg_p *msg) Post(718,taskName,int2str(Urank),devTypName); return AddressBook::ERR_INVALID_DEVTYPE; } - // FILE* dbgSBase = fopen("Root2NS_SBase_devices.txt","a"); // DEBUG ONLY vector devNames; - // vector devData; vector devAddrs; int numSupers = 1; // get method expects a *reference* to the count SymAddr_t* devSuper = msg->Get(3,numSupers); @@ -505,7 +528,6 @@ unsigned SBase::DataDevice(PMsg_p *msg) } vector devAttrs; msg->GetX(0, devNames); - // msg->Get(1, devData); msg->Get(1, devAddrs); if (devNames.size() != devAddrs.size()) { @@ -518,17 +540,6 @@ unsigned SBase::DataDevice(PMsg_p *msg) Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAttrs.size()),"attribute"); return ERR_DEVICE_DATA_MISMATCH; } - // if (devNames.size() != devData.size()) - // { - // Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devData.size())); - // return ERR_DEVICE_DATA_MISMATCH; - // } - /* - fprintf(dbgSBase, "Nameserver received device info for %u devices\n", devNames.size()); - fprintf(dbgSBase, "\n"); - fprintf(dbgSBase, "________________________________________________________________________________\n"); - fprintf(dbgSBase, "\n"); - */ unsigned err = AddressBook::SUCCESS; for (unsigned d = 0; d < devNames.size(); d++) { @@ -538,23 +549,6 @@ unsigned SBase::DataDevice(PMsg_p *msg) devIdx, RecordType_t(Device), devAttrs[d]); - // Record_t device(devNames[d], - // devData[d].Address, - // devData[d].RecordType == Supervisor ? static_cast(devData[d].Rank) : devData[d].Supervisor, - // devData[d].DeviceType, - // devData[d].RecordType, - // devData[d].Attribute); - /* - fprintf(dbgSBase, "Device %s:\n", device.Name.c_str()); - fprintf(dbgSBase, "Address: 0x%.16llx\n", device.Address); - if (device.RecordType == Supervisor) - fprintf(dbgSBase, "Supervisor Rank: %d\n", device.Rank); - else fprintf(dbgSBase, "Supervisor Address: 0x%.16llx\n", device.Supervisor); - fprintf(dbgSBase, "Device type index: %d\n", device.DeviceType); - fprintf(dbgSBase, "Attributes: %d\n", device.Attribute); - fprintf(dbgSBase, "Record type: %d\n", static_cast(device.RecordType)); - fprintf(dbgSBase, "\n"); - */ if ((err = AddDevice(taskName, device)) != AddressBook::SUCCESS) { if (ClearTask(taskName) != AddressBook::SUCCESS) @@ -568,13 +562,12 @@ unsigned SBase::DataDevice(PMsg_p *msg) } if (TaskState(taskName) < Linked) { - if (err = TaskState(taskName,Linked)) + if ((err = TaskState(taskName,Linked))) { Post(724,taskName,int2str(Urank)); return err; } } - // fclose(dbgSBase); return AddressBook::SUCCESS; } @@ -617,9 +610,7 @@ unsigned SBase::DataSupervisor(PMsg_p *msg) Post(718,taskName,int2str(Urank),devTypName); return AddressBook::ERR_INVALID_DEVTYPE; } - // FILE* dbgSBase = fopen("Root2NS_SBase_devices.txt","a"); // DEBUG ONLY - vector devNames; - // vector devData; + vector devNames;; vector devAddrs; vector devRanks; vector devAttrs; @@ -643,17 +634,6 @@ unsigned SBase::DataSupervisor(PMsg_p *msg) Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devRanks.size()),"rank"); return ERR_DEVICE_DATA_MISMATCH; } - // if (devNames.size() != devData.size()) - // { - // Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devData.size())); - // return ERR_DEVICE_DATA_MISMATCH; - // } - /* - fprintf(dbgSBase, "Nameserver received supervisor info for %u supervisors\n", devNames.size()); - fprintf(dbgSBase, "\n"); - fprintf(dbgSBase, "________________________________________________________________________________\n"); - fprintf(dbgSBase, "\n"); - */ for (unsigned d = 0; d < devNames.size(); d++) { Record_t device(devNames[d], @@ -662,23 +642,6 @@ unsigned SBase::DataSupervisor(PMsg_p *msg) devIdx, RecordType_t(Supervisor), devAttrs[d]); - // Record_t device(devNames[d], - // devData[d].Address, - // devData[d].RecordType == Supervisor ? static_cast(devData[d].Rank) : devData[d].Supervisor, - // devData[d].DeviceType, - // devData[d].RecordType, - // devData[d].Attribute); - /* - fprintf(dbgSBase, "Device %s:\n", device.Name.c_str()); - fprintf(dbgSBase, "Address: 0x%.16llx\n", device.Address); - if (device.RecordType == Supervisor) - fprintf(dbgSBase, "Supervisor Rank: %d\n", device.Rank); - else fprintf(dbgSBase, "Supervisor Address: 0x%.16llx\n", device.Supervisor); - fprintf(dbgSBase, "Device type index: %d\n", device.DeviceType); - fprintf(dbgSBase, "Attributes: %d\n", device.Attribute); - fprintf(dbgSBase, "Record type: %d\n", static_cast(device.RecordType)); - fprintf(dbgSBase, "\n"); - */ unsigned err = AddressBook::SUCCESS; if ((err = AddDevice(taskName, device)) != AddressBook::SUCCESS) { @@ -710,7 +673,7 @@ unsigned SBase::DumpAll(PMsg_p *msg, unsigned comm) else dumpFile = fopen(filename.c_str(), "a"); vector tasks; unsigned err = AddressBook::SUCCESS; - if (err = ListTask(tasks)) + if ((err = ListTask(tasks))) { Post(709, int2str(Urank)); if (dumpFile != stdout) fclose(dumpFile); @@ -736,7 +699,7 @@ unsigned SBase::DumpTask(PMsg_p *msg, unsigned comm) string taskName = msg->Zname(0); if (taskName.empty()) { - Post(710, int2str(Urank)); + Post(710, "Dump", int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } string filename = msg->Zname(1); @@ -750,47 +713,458 @@ unsigned SBase::DumpTask(PMsg_p *msg, unsigned comm) unsigned SBase::QueryAttr(PMsg_p *msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + if (taskName.empty()) + { + Post(710, "QueryByAttribute", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + unsigned err = SUCCESS; + vector attrs; + msg->GetX(2,attrs); + int srcProc = msg->Src(); + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + vector devAddrs; + vector devNames; + vector devAttrs; + WALKVECTOR(string, attrs, a) + { + const RecordVect_t* devRecords; + if ((err = FindByAttribute(taskName, *a, devRecords))) + { + if (err == ERR_TASK_NOT_FOUND || err == ERR_INVALID_MAP) + { + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return SUCCESS; + } + continue; + } + devAttrs.push_back(*a); + for (vector::const_iterator device = devRecords->begin(); device != devRecords->end(); device++) + { + devAddrs.push_back((*device)->Address); + devNames.push_back((*device)->Name); + } + } + if (!devAddrs.size()) msg->L(3, Q::NF); + else + { + msg->Put(0,&devAddrs); + msg->PutX(1,&devNames); + msg->PutX(2,&devAttrs); + } + msg->Send(srcProc); + return SUCCESS; } +// Gets all devices for a task. We return every device in a single PMsg_p. This could get +// VERY big, but since there are no certainties the receiving device knows how many devices +// it's expecting, chunking the reply into bite-sized messages makes it impossible for the +// receiver to know when it can stop receiving. An alternative would be to insert the +// device count in the message, along with possibly a sequence number, but for a first +// revision this is just adding complication for possibly little benefit. To be tested. unsigned SBase::QueryDevIAll(PMsg_p *msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + if (taskName.empty()) + { + Post(710, "QueryAllDevices", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + vector devAddrs; + vector devNames; + const vector* devRecords; + if ((err = GetDevices(taskName, devRecords))) + { + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return SUCCESS; + } + for (vector::const_iterator device = devRecords->begin(); device != devRecords->end(); device++) + { + devAddrs.push_back(device->Address); + devNames.push_back(device->Name); + } + if (!devAddrs.size()) msg->L(3, Q::NF); + else + { + msg->Put(0,&devAddrs); + msg->PutX(1,&devNames); + } + msg->Send(srcProc); + return SUCCESS; } +// Query device by name. A question remains here - how do we deal with the queries +// asking for a group? unsigned SBase::QueryDevIByName(PMsg_p *msg, unsigned comm) { - return 0; + if (!((msg->L(3) == Q::NGRP) || (msg->L(3) == Q::NM) || (msg->L(3) == Q::NSUP))) + { + Post(702,uint2str(msg->Key()),int2str(Urank)); + return ERR_NONFATAL; + } + string taskName = msg->Zname(0); + if (taskName.empty()) + { + Post(710, "QueryDeviceByName", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + string deviceName = msg->Zname(1); + if (deviceName.empty()) + { + Post(731, int2str(Urank)); + return ERR_INVALID_DEVICE; + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + const Record_t* device; + if ((err = FindDevice(taskName, deviceName, device))) + { + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return SUCCESS; + } + if (msg->L(3) == Q::NGRP) + { + vector devAddrs; + vector devNames; + const RecordVect_t* devRecords; + if ((err = FindBySuper(taskName, device->Supervisor, devRecords))) + { + // given that at least the lookup device must belong to its Supervisor, finding + // no devices attached to the Supervisor in question would be a serious error! + Post(732,int2str(Urank),uint2str(device->Supervisor),device->Name,err == ERR_TASK_NOT_FOUND ? "task" : "devices"); + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return ERR_DEVICE_DATA_MISMATCH; + } + for (vector::const_iterator deviceIt = devRecords->begin(); deviceIt != devRecords->end(); deviceIt++) + { + devAddrs.push_back((*deviceIt)->Address); + devNames.push_back((*deviceIt)->Name); + } + if (!devAddrs.size()) msg->L(3, Q::NF); + else + { + msg->Put(0,&devAddrs); + msg->PutX(1,&devNames); + } + } + else + { + // these have to be copied into a temporary (and then recopied into the message) + // because Put expects a type match, while the iterator returns constant records, + // and Put itself must overwrite the actual value in its own data structure (not + // the original) - but if its internal type is , it can't overwrite. + SymAddr_t nonConstDevAddr = device->Address; + SymAddr_t nonConstSuperAddr = device->Supervisor; + msg->Put(0,&nonConstDevAddr); + if (msg->L(3) == Q::NSUP) msg->Put(1,&nonConstSuperAddr); + } + msg->Send(srcProc); + return SUCCESS; } unsigned SBase::QueryDevIByID(PMsg_p *msg, unsigned comm) { - return 0; + if (!((msg->L(3) == Q::IGRP) || (msg->L(3) == Q::ID) || (msg->L(3) == Q::ISUP))) + { + Post(702,uint2str(msg->Key()),int2str(Urank)); + return ERR_NONFATAL; + } + string taskName = msg->Zname(0); + if (taskName.empty()) + { + Post(710, "QueryDeviceByID", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + int devCount = 0; + SymAddr_t* deviceAddr = msg->Get(0,devCount); + if (!devCount) + { + Post(731, int2str(Urank)); + return ERR_INVALID_DEVICE; + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + const Record_t* device; + if ((err = FindDevice(taskName, *deviceAddr, device))) + { + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return SUCCESS; + } + if (msg->L(3) == Q::IGRP) + { + vector devAddrs; + vector devNames; + const RecordVect_t* devRecords; + if ((err = FindBySuper(taskName, device->Supervisor, devRecords))) + { + // given that at least the lookup device must belong to its Supervisor, finding + // no devices attached to the Supervisor in question would be a serious error! + Post(732,int2str(Urank),uint2str(device->Supervisor),device->Name,err == ERR_TASK_NOT_FOUND ? "task" : "devices"); + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return ERR_DEVICE_DATA_MISMATCH; + } + for (vector::const_iterator deviceIt = devRecords->begin(); deviceIt != devRecords->end(); deviceIt++) + { + devAddrs.push_back((*deviceIt)->Address); + devNames.push_back((*deviceIt)->Name); + } + if (!devAddrs.size()) msg->L(3, Q::NF); + else + { + msg->Put(0,&devAddrs); + msg->PutX(1,&devNames); + } + } + else + { + msg->Zname(1,device->Name); + if (msg->L(3) == Q::ISUP) + { + SymAddr_t nonConstSuperAddr = device->Supervisor; + msg->Put(1,&nonConstSuperAddr); + } + } + msg->Send(srcProc); + return SUCCESS; } unsigned SBase::QueryDevT(PMsg_p *msg, unsigned comm) { - return 0; + if (!((msg->L(3) == Q::IN) || (msg->L(3) == Q::NM) || (msg->L(3) == Q::OUT))) + { + Post(702,uint2str(msg->Key()),int2str(Urank)); + return ERR_NONFATAL; + } + string taskName = msg->Zname(0); + if (taskName.empty()) + { + Post(710, "QueryDeviceByName", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + string lookupType = msg->Zname(2); + if (lookupType.empty()) + { + if (msg->L(3) == Q::NM) + { + Post(706, "find",taskName,int2str(Urank)); + return ERR_INVALID_DEVTYPE; + } + else + { + Post(733,int2str(Urank)); + return ERR_INVALID_MESSAGE_TYPE; + } + + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + vector devAddrs; + vector devNames; + const RecordVect_t* devRecords; + if (msg->L(3) == Q::NM) err = FindByType(taskName,lookupType,devRecords); + else if (msg->L(3) == Q::IN) err = FindByInMsg(taskName,lookupType,devRecords); + else err = FindByOuMsg(taskName,lookupType,devRecords); + if (err) + { + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return SUCCESS; + } + for (vector::const_iterator device = devRecords->begin(); device != devRecords->end(); device++) + { + devAddrs.push_back((*device)->Address); + devNames.push_back((*device)->Name); + } + if (!devAddrs.size()) msg->L(3, Q::NF); + else + { + msg->Put(0,&devAddrs); + msg->PutX(1,&devNames); + } + msg->Send(srcProc); + return SUCCESS; + } unsigned SBase::QueryExtn(PMsg_p *msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + if (taskName.empty()) + { + Post(710, "QueryAllExternals", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + vector devAddrs; + vector devNames; + const vector* devRecords; + if ((err = GetExternals(taskName, devRecords))) + { + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return SUCCESS; + } + for (vector::const_iterator device = devRecords->begin(); device != devRecords->end(); device++) + { + devAddrs.push_back(device->Address); + devNames.push_back(device->Name); + } + if (!devAddrs.size()) msg->L(3, Q::NF); + else + { + msg->Put(0,&devAddrs); + msg->PutX(1,&devNames); + } + msg->Send(srcProc); + return SUCCESS; } +// ask for a list of tasks on this SBase. Can't fail: either we have tasks or not +// so we just return a list if we have them, a not found message if not. unsigned SBase::QueryList(PMsg_p *msg, unsigned comm) { - return 0; + unsigned err = SUCCESS; + int srcProc = msg->Src(); + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + vector taskNames; + if ((err = ListTask(taskNames)) || (taskNames.size() == 0)) msg->L(3,Q::TNF); + else msg->PutX(1,&taskNames); + msg->Send(srcProc); + return SUCCESS; } +// Get all supervisors for a task. We have one more list in addition to the +// address and the name: the list of ranks, because the requesting process +// may wish to identify a Supervisor by MPI rank rather than POETS address. unsigned SBase::QuerySupv(PMsg_p *msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + if (taskName.empty()) + { + Post(710, "QueryAllSupervisors", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + vector devAddrs; + vector devNames; + vector devRanks; + const vector* devRecords; + if ((err = GetSupervisors(taskName, devRecords))) + { + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); + return SUCCESS; + } + for (vector::const_iterator device = devRecords->begin(); device != devRecords->end(); device++) + { + devAddrs.push_back(device->Address); + devNames.push_back(device->Name); + devRanks.push_back(device->Rank); + } + if (!devAddrs.size()) msg->L(3, Q::NF); + else + { + msg->Put(0,&devAddrs); + msg->PutX(1,&devNames); + msg->Put(2,&devRanks); + } + msg->Send(srcProc); + return SUCCESS; } +// A task query might end up being some set of requests with further internal details +// in future, but for the moment we simply return summary task info. unsigned SBase::QueryTask(PMsg_p *msg, unsigned comm) { - return 0; + string taskName = msg->Zname(0); + if (taskName.empty()) + { + Post(710, "QueryTask", int2str(Urank)); + return AddressBook::ERR_INVALID_TASK; + } + unsigned err = SUCCESS; + int srcProc = msg->Src(); + msg->comm = Comms[comm]; + msg->Src(msg->Tgt()); + msg->L(1,Q::RPLY); + TaskData_t task; + if ((err = GetTask(taskName, task))) + { + msg->L(3,Q::TNF); + msg->Send(srcProc); + return SUCCESS; + } + switch (task.State) + { + case Loaded: + msg->Zname(1,"Loaded"); + break; + case Linked: + msg->Zname(1,"Linked"); + break; + case Built: + msg->Zname(1,"Built"); + break; + case Deployed: + msg->Zname(1,"Deployed"); + break; + case Init: + msg->Zname(1,"Init"); + break; + case Running: + msg->Zname(1,"Running"); + break; + case Finished: + msg->Zname(1,"Finished"); + break; + default: + Post(734,taskName,int2str(Urank)); + msg->Zname(1,"Unknown"); + } + msg->Zname(2,task.Path); + msg->Zname(3,task.XML); + msg->PutX(0,&task.MessageTypes); + msg->PutX(1,&task.AttributeTypes); + vector counts; + counts.push_back(task.DeviceCount); + counts.push_back(task.ExternalCount); + counts.push_back(task.SupervisorCount); + msg->Put(2,&counts); + msg->Send(srcProc); + return SUCCESS; } unsigned SBase::SendAttr(PMsg_p *msg, unsigned comm) @@ -827,3 +1201,38 @@ unsigned SBase::SendSupv(PMsg_p *msg, unsigned comm) { return 0; } + +string SBase::State2Str(TaskState_t state) +{ + switch (state) + { + case Loaded: + return "Loaded"; + case Linked: + return "Linked"; + case Built: + return "Built"; + case Deployed: + return "Deployed"; + case Init: + return "Init"; + case Running: + return "Running"; + case Finished: + return "Finished"; + } + return "Unknown"; +} + +TaskState_t SBase::Str2State(const string& state) +{ + + if (state == "Loaded") return Loaded; + if (state == "Linked") return Linked; + if (state == "Built") return Built; + if (state == "Deployed") return Deployed; + if (state == "Init") return Init; + if (state == "Running") return Running; + if (state == "Finished") return Finished; + return Unknown; +} diff --git a/Source/NameServer/SBase.h b/Source/NameServer/SBase.h index de2fee35..d98cffc0 100644 --- a/Source/NameServer/SBase.h +++ b/Source/NameServer/SBase.h @@ -24,6 +24,9 @@ unsigned OnQuery (PMsg_p *, unsigned); unsigned OnReply (PMsg_p *, unsigned); unsigned OnSend (PMsg_p *, unsigned); +static string State2Str (TaskState_t); +static TaskState_t Str2State (const string&); + protected: virtual unsigned Connect (string=""); diff --git a/Source/OrchestratorMessages.txt b/Source/OrchestratorMessages.txt index 5990a1cc..5ed74f91 100644 --- a/Source/OrchestratorMessages.txt +++ b/Source/OrchestratorMessages.txt @@ -104,11 +104,11 @@ 703(E) : "Unexpected reply to query: %s received at SBase process rank %s. Circular routing loop?" 704(E) : "Tried to load task into SBase at rank %s with no name" 705(E) : "Error while loading SBase data at rank %s: no task name" -706(E) : "Error loading device type data for task %s into SBase at rank %s: no device type name" +706(E) : "Error loading device type data for task %s in SBase at rank %s: no device type name" 707(E) : "Error loading device %s for task %s into SBase at rank %s" 708(E) : "Error loading device %s for task %s into SBase at rank %s: no such task" 709(W) : "Unable to register supervisor %s on box %s into SBase: not found in the rank list. May need to connect" -710(W) : "Empty task for dump request on SBase at rank %s" +710(W) : "Empty task for SBase %s request at rank %s" 711(S) : "Error: no nameserver found" 712(U) : "Fatal error: nameserver on nonexistent comm" 713(I) : "Sending information to nameserver rank %s from root rank %s on devices on thread %s" @@ -128,6 +128,13 @@ 728(E) : "Error: SBase at rank %s has a record for Supervisor rank %s, but no such Supervisor exists" 729(E) : "Supervisor %s registered as a device for Supervisor %s, but only one Supervisor can exist" 730(W) : "Invalid command %s to SBase rank %s: missing value %s" +731(E) : "Query made for a nameless or nonexistent device on SBase rank %s" +732(S) : "SBase at rank %s shows a Supervisor %s for device %s, but no associated %s" +733(E) : "Error looking up by message type on SBase at rank %s: no message type given" +734(W) : "Task %s is in an unknown state according to SBase at rank %s" +735(E) : "No device address received for device name %s to give to process at rank %s" +736(E) : "No associated supervisor address located for device %s to give to process at rank %s" +737(E) : "Mismatch in supervisor data received at rank %s from SBase: %s addresses, %s names, %s ranks" 801(D) : "P_builder::Add(name=%s,file=%s)" 802(D) : "Removing task %s from the Orchestrator" From 0d3c8032a9bc669f54f64c18579d53106bb41a68 Mon Sep 17 00:00:00 2001 From: AlexRast Date: Mon, 16 Sep 2019 16:58:04 +0100 Subject: [PATCH 05/14] Commenting and some small fixes --- Source/Mothership/MothershipDummy.cpp | 10 +- Source/Mothership/MothershipDummy.h | 2 +- Source/NameServer/SBase.cpp | 811 ++++++++++++++++++-------- Source/NameServer/SBase.h | 32 +- Source/OrchestratorMessages.txt | 2 +- 5 files changed, 604 insertions(+), 253 deletions(-) diff --git a/Source/Mothership/MothershipDummy.cpp b/Source/Mothership/MothershipDummy.cpp index 8a41c23d..610a6b5c 100644 --- a/Source/Mothership/MothershipDummy.cpp +++ b/Source/Mothership/MothershipDummy.cpp @@ -500,7 +500,13 @@ void MothershipDummy::MapValue2Key(unsigned value, vector* key) { uint32_t val = static_cast(value); void* valPtr = static_cast(&val); - for (unsigned b=0; b < sizeof(uint32_t); b++) + // another reason to hate endianness: although the Key method in Msg_p + // computes the key by successive left-shifting, the value as an + // unsigned is stored little-endian. Which means that left shift + // really means byte-wise right shift, from the POV of the underlying + // representation in memory. So it's necessary to count back through + // the unsigned value. + for (int b=sizeof(uint32_t)-1; b >= 0; b--) key->push_back(static_cast(valPtr)[b]); } @@ -508,7 +514,7 @@ string MothershipDummy::QueryType(unsigned keyVal) { vector key; MapValue2Key(keyVal, &key); - if ((key[0] != Q::NAME) || (key[1] != Q::QRY)) return "Not a Query"; + if ((key[0] != Q::NAME) || ((key[1] != Q::QRY) && (key[1] != Q::RPLY))) return "Not a Query"; switch (key[2]) { case Q::DEVI: diff --git a/Source/Mothership/MothershipDummy.h b/Source/Mothership/MothershipDummy.h index 1f8362c0..d9212a50 100644 --- a/Source/Mothership/MothershipDummy.h +++ b/Source/Mothership/MothershipDummy.h @@ -51,7 +51,7 @@ class MothershipDummy : public SBase unsigned ReplyTask(PMsg_p*, unsigned); static void MapValue2Key(unsigned value, vector* key); - string QueryType(unsigned); + static string QueryType(unsigned); typedef unsigned (MothershipDummy::*pMeth)(PMsg_p *, unsigned); typedef map FnMap_t; diff --git a/Source/NameServer/SBase.cpp b/Source/NameServer/SBase.cpp index ef1e739a..e9b374c1 100644 --- a/Source/NameServer/SBase.cpp +++ b/Source/NameServer/SBase.cpp @@ -3,10 +3,13 @@ #include "Pglobals.h" #include // debug + +// constructor. Arguments will almost always come from higher-level +// constructors since we expect to derive from SBase. SBase::SBase(int argc,char ** argv,string d,string s): CommonBase(argc,argv,d,s), AddressBook(d) { FnMapx.push_back(new FnMap_t); // create a default function table (for the *nameserver base* class!) - // Load event handler map + // Load event handler map (we need to handle a fair number of different SBase requests!) (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NM )] = &SBase::OnSend; (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ID )] = &SBase::OnSend; (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ALL )] = &SBase::OnSend; @@ -69,76 +72,97 @@ SBase::SBase(int argc,char ** argv,string d,string s): CommonBase(argc,argv,d,s) (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &SBase::OnDump; (*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &SBase::OnDump; } - + +// destructor only needs to get rid of function tables. SBase::~SBase() { WALKVECTOR(FnMap_t*,FnMapx,F) // WALKVECTOR and WALKMAP are in macros.h (long include chain) delete *F; // get rid of derived class function tables } +//============================================================================== +/* + OnCfg - handle Mothership configuration commands +*/ +//============================================================================== unsigned SBase::OnCfg(PMsg_p *msg, unsigned comm) { switch(msg->L(2)) { case Q::DIST: - return ConfigDistribute(msg, comm); + return ConfigDistribute(msg, comm); // send core data to Motherships case Q::TDIR: - return ConfigDir(msg, comm); + return ConfigDir(msg, comm); // send binary data directory to Motherships case Q::BLD: - return ConfigBuild(msg, comm); + return ConfigBuild(msg, comm); // rebuild this SBase's name database case Q::RECL: - return ConfigRecall(msg, comm); + return ConfigRecall(msg, comm); // recall (remove) a task from Motherships case Q::DEL: - return ConfigDelete(msg, comm); + return ConfigDelete(msg, comm); // delete a task from this SBase case Q::STATE: - return ConfigState(msg, comm); + return ConfigState(msg, comm); // Update task running state default: Post(700,uint2str(msg->Key()),int2str(Urank)); return 0; } } +//============================================================================== +/* + OnCmd - handle low-level SBase commands +*/ +//============================================================================== unsigned SBase::OnCmd(PMsg_p *msg, unsigned comm) { switch(msg->L(2)) { case Q::INTG: - return CmdIntegrity(msg); + return CmdIntegrity(msg); // integrity check (a long operation) case Q::MONI: - return 0; + return 0; // monitor function not yet implemented case Q::LOG: - return 0; + return 0; // nameserver logging not yet implemented default: Post(700,uint2str(msg->Key()),int2str(Urank)); return 0; } } +//============================================================================== +/* + OnCmd - load name data to this SBase +*/ +//============================================================================== unsigned SBase::OnData(PMsg_p *msg, unsigned comm) { switch(msg->L(2)) { case Q::TASK: - return DataTask(msg); + return DataTask(msg); // general task data. Needed before we can load devices. case Q::DEVT: - return DataDevType(msg); + return DataDevType(msg); // information on device types case Q::DEVI: - return DataDevice(msg); + return DataDevice(msg); // device name-address mappings case Q::DEVE: - return DataDeviceExternal(msg); + return DataDeviceExternal(msg); // devices with an external connection case Q::EXTN: - return DataExternal(msg); + return DataExternal(msg); // external device name-address mappings case Q::SUPV: - return DataSupervisor(msg); + return DataSupervisor(msg); // supervisor address-MPI rank mappings default: Post(700,uint2str(msg->Key()),int2str(Urank)); return 0; } } +//============================================================================== +/* + OnDump - dump out this SBase's database info, probably to a file +*/ +//============================================================================== unsigned SBase::OnDump(PMsg_p *msg, unsigned comm) { - if (msg->L(2) == Q::LIST) return DumpSummary(msg, comm); + if (msg->L(2) == Q::LIST) return DumpSummary(msg, comm); // just a task summary if (msg->L(2) != Q::TASK) { Post(700,uint2str(msg->Key()),int2str(Urank)); @@ -147,47 +171,52 @@ unsigned SBase::OnDump(PMsg_p *msg, unsigned comm) switch(msg->L(3)) { case Q::ALL: - return DumpAll(msg, comm); + return DumpAll(msg, comm); // comprehensive info on all tasks case Q::NM: - return DumpTask(msg, comm); + return DumpTask(msg, comm); // info only on a named task default: Post(700,uint2str(msg->Key()),int2str(Urank)); return 0; } } +//============================================================================== +/* + OnQuery - respond to name services queries (most of the functionality is here) +*/ +//============================================================================== unsigned SBase::OnQuery(PMsg_p *msg, unsigned comm) { - switch(msg->L(2)) + switch(msg->L(2)) // messages have several subkeys for queries { - case Q::DEVI: + case Q::DEVI: // queries relating to devices switch(msg->L(3)) { - case Q::NM: - case Q::NGRP: - case Q::NSUP: - return QueryDevIByName(msg, comm); - case Q::ID: - case Q::IGRP: - case Q::ISUP: - return QueryDevIByID(msg, comm); - case Q::ALL: + case Q::NM: // a single device + case Q::NGRP: // devices in the same group as a device + case Q::NSUP: // supervisor of device + return QueryDevIByName(msg, comm); // query by name + case Q::ID: // a single device + case Q::IGRP: // devices in the same group as a device + case Q::ISUP: // supervisor of device + return QueryDevIByID(msg, comm); // query by device ID + case Q::ALL: // just get all (normal) devices return QueryDevIAll(msg, comm); default: Post(702,uint2str(static_cast(msg->L(3))),int2str(Urank)); return 0; } - case Q::SUPV: + case Q::SUPV: // query supervisors return QuerySupv(msg, comm); - case Q::EXTN: + case Q::EXTN: // query externals return QueryExtn(msg, comm); - case Q::DEVT: + case Q::DEVT: // query devices by a named type return QueryDevT(msg, comm); - case Q::ATTR: - return QueryAttr(msg, comm); - case Q::LIST: + case Q::ATTR: // query devices by a labelled attribute + return QueryAttr(msg, comm); + case Q::LIST: // get a basic list of tasks return QueryList(msg, comm); - case Q::TASK: + case Q::TASK: // get summary info on a task or all tasks return QueryTask(msg, comm); default: Post(700,uint2str(msg->Key()),int2str(Urank)); @@ -195,14 +224,28 @@ unsigned SBase::OnQuery(PMsg_p *msg, unsigned comm) } } +//============================================================================== +/* + OnReply - receive a name services response. SBase just immediately returns, + because we expect this to be sent FROM an SBase TO some querying process. +*/ +//============================================================================== unsigned SBase::OnReply(PMsg_p *msg, unsigned comm) { Post(703,uint2str(msg->Key()),int2str(Urank)); return 0; } +//============================================================================== +/* + OnSend - forward packets to some set of devices. Not implemented to devices yet. +*/ +//============================================================================== unsigned SBase::OnSend(PMsg_p *msg, unsigned comm) { + // OnSend uses the same subtree as OnQuery, except we don't need to handle a + // Q::TASK subkey. It will in practice execute a query and then forward + // the message to a Mothership for further handling. switch(msg->L(2)) { case Q::DEVI: @@ -236,6 +279,13 @@ unsigned SBase::OnSend(PMsg_p *msg, unsigned comm) } } +//============================================================================== +/* + Connect - set up new function tables for handling messages from another MPI + universe, such as, e.g. might happen if a group of Motherships were started + and connected to the main Orchestrator universe as a service. +*/ +//============================================================================== unsigned SBase::Connect(string svc) { unsigned connErr = MPI_SUCCESS; @@ -307,68 +357,92 @@ unsigned SBase::Connect(string svc) return MPI_SUCCESS; } +//============================================================================== +/* + CmdIntegrity - handle an integrity check request. Not implemented yet. +*/ +//============================================================================== unsigned SBase::CmdIntegrity(PMsg_p *msg) { return 0; } +//============================================================================== +/* + ConfigBuild - rebuild SBase, probably because device data has changed +*/ +//============================================================================== unsigned SBase::ConfigBuild(PMsg_p *msg, unsigned comm) { - string taskName = msg->Zname(0); + string taskName = msg->Zname(0); // get the task to be rebuilt vector tasks; unsigned err = SUCCESS; - if (taskName.empty()) + if (taskName.empty()) // No task name => rebuild everything { - if ((err = ListTask(tasks))) + if ((err = ListTask(tasks))) // Some tasks exist to rebuild? { - Post(721,"rebuild","list",int2str(Urank)); + Post(721,"rebuild","list",int2str(Urank)); // No. Warn the user. return err; } } - else tasks.push_back(taskName); + else tasks.push_back(taskName); // a named task. Only rebuild that one. vector::iterator task; - for (task = tasks.begin(); task != tasks.end(); task++) + // traverse the found task list, + for (task = tasks.begin(); task != tasks.end(); task++) { - if (!(err = RebuildTask(*task))) + if (!(err = RebuildTask(*task))) // try to rebuild both the task { - if (!(err = BuildMaps(*task))) + if (!(err = BuildMaps(*task))) // and its crosslinks err = BuildLink(*task); } - if (err) + if (err) // something went wrong in the rebuild { - Post(721,"rebuild",*task,int2str(Urank)); + Post(721,"rebuild",*task,int2str(Urank)); // so warn the user. return err; } } return err; } - + +//============================================================================== +/* + ConfigDelete - remove a task from this SBase +*/ +//============================================================================== unsigned SBase::ConfigDelete(PMsg_p *msg, unsigned comm) { - string taskName = msg->Zname(0); + string taskName = msg->Zname(0); // get task name vector tasks; unsigned err = SUCCESS; - if (taskName.empty()) + if (taskName.empty()) // no task name => delete all tasks { - if ((err = ListTask(tasks))) + if ((err = ListTask(tasks))) // so get all tasks that have been loaded { - Post(721,"gett","list",int2str(Urank)); + Post(721,"gett","list",int2str(Urank)); // warning if there are none return err; } } - else tasks.push_back(taskName); + else tasks.push_back(taskName); // otherwise delete only the named task vector::iterator task; for (task = tasks.begin(); task != tasks.end(); task++) { - if ((err = DelTask(*task))) + if ((err = DelTask(*task))) // try to delete all tasks we asked for { - Post(721,"delet",*task,int2str(Urank)); + Post(721,"delet",*task,int2str(Urank)); // warning on failure return err; } } return err; } +//============================================================================== +/* + ConfigDistribute - send core mappings to Motherships. Normally this will + be handled from Root so SBase itself needs to do very little, only make + sure the task state is changed to Deployed to reflect the new state of + affairs. +*/ +//============================================================================== unsigned SBase::ConfigDistribute(PMsg_p *msg, unsigned comm) { string taskName = msg->Zname(0); @@ -380,10 +454,16 @@ unsigned SBase::ConfigDistribute(PMsg_p *msg, unsigned comm) return SUCCESS; } +//============================================================================== +/* + ConfigDir - send task binary directory path (mostly to Motherships). SBase + keeps a copy of this, though, so this function does update it +*/ +//============================================================================== unsigned SBase::ConfigDir(PMsg_p *msg, unsigned comm) { - string taskName = msg->Zname(0); - string taskPath = msg->Zname(1); + string taskName = msg->Zname(0); // first static name string is the task name + string taskPath = msg->Zname(1); // and second name string is the directory path if (taskPath.empty()) Post(723,int2str(Urank)); if (TaskExecPath(taskName,taskPath)) { @@ -393,16 +473,23 @@ unsigned SBase::ConfigDir(PMsg_p *msg, unsigned comm) return SUCCESS; } +//============================================================================== +/* + ConfigRecall - remove knowledge of a task from a Mothership. This does not + entirely delete the task from SBase or name services, but it does mean that] + a task should be redeployed if it is to be run again. +*/ +//============================================================================== unsigned SBase::ConfigRecall(PMsg_p *msg, unsigned comm) { - string taskName = msg->Zname(0); + string taskName = msg->Zname(0); // as usual, get the task name vector tasks; unsigned err = SUCCESS; - if (taskName.empty()) + if (taskName.empty()) // where no name => recall all tasks { - if ((err = ListTask(tasks))) + if ((err = ListTask(tasks))) // which we should get if they exist { - Post(721,"gett","list",int2str(Urank)); + Post(721,"gett","list",int2str(Urank)); // and warn if not return err; } } @@ -410,13 +497,13 @@ unsigned SBase::ConfigRecall(PMsg_p *msg, unsigned comm) vector::iterator task; for (task = tasks.begin(); task != tasks.end(); task++) { - if ((err = ClearTask(*task))) - { + if ((err = ClearTask(*task))) // easiest way to reset the task on SBase + { // is to clear it and reset it Post(721,"clear",*task,int2str(Urank)); return err; } - if ((err = TaskState(*task,Unknown))) - { + if ((err = TaskState(*task,Unknown))) // a recalled task is in unknown state + { // until we redeploy or rebuild Post(725,*task,int2str(Urank)); return err; } @@ -424,6 +511,14 @@ unsigned SBase::ConfigRecall(PMsg_p *msg, unsigned comm) return err; } +//============================================================================== +/* + ConfigState - SBases maintain copies of the task run state. After a task has + been deployed, this corresponds to the Mothership's state for that task. + Before it has been deployed, it corresponds to the stage in the build + process that has been reached at Root. +*/ +//============================================================================== unsigned SBase::ConfigState(PMsg_p *msg, unsigned comm) { string taskName = msg->Zname(0); @@ -449,77 +544,107 @@ unsigned SBase::ConfigState(PMsg_p *msg, unsigned comm) return SUCCESS; } +//============================================================================== +/* + DataTask - Load this SBase with basic task data. A task is loaded from a + PMsg_p containing its name, path, XML filename, a vector of + message types (strings), a vector of attribute types (also strings), and + a pair of counts (how many devices/externals there are for the ENTIRE task). +*/ +//============================================================================== unsigned SBase::DataTask(PMsg_p *msg) { TaskData_t task; - task.Name = msg->Zname(0); + task.Name = msg->Zname(0); // extract all the basic information task.Path = msg->Zname(1); task.XML = msg->Zname(2); - if (task.Name == "") + if (task.Name == "") // tasks must have a name { Post(704, int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } - msg->GetX(0, task.MessageTypes); + msg->GetX(0, task.MessageTypes); // then get the message and attribute types msg->GetX(1, task.AttributeTypes); vector counts; - msg->Get(2, counts); + msg->Get(2, counts); // and the device counts if (counts.size() != 2) Post(720,uint2str(counts.size()),"2",task.Name,int2str(Urank)); else { task.DeviceCount = counts[0]; task.ExternalCount = counts[1]; } - return AddTask(task.Name, task); + return AddTask(task.Name, task); // add the task (through AddressBook) } +//============================================================================== +/* + DataDevType - Load this task with DeviceType info. This is done as a + separate message to the rest of the basic task data because a DeviceType + has more information about pins and messages than a simple string, so it's + easier to separate it into its own message rather than serialising the + complexity into the basic task info message. Message types for in and out + pins are sent as indices into the message types which were received in the + previous task message, and which should also match the order of declaration + in the XML. +*/ +//============================================================================== unsigned SBase::DataDevType(PMsg_p *msg) { DevTypeRecord_t deviceType; - string taskName = msg->Zname(0); - deviceType.Name = msg->Zname(2); - if (taskName == "") + string taskName = msg->Zname(0); // first static field is the task + deviceType.Name = msg->Zname(2); // and third is the device type + if (taskName == "") // no task name is an error { Post(705, int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } - if (deviceType.Name == "") + if (deviceType.Name == "") // similarly for no device type { - Post(706, taskName, int2str(Urank)); + Post(706, "load", taskName, int2str(Urank)); return AddressBook::ERR_INVALID_DEVTYPE; } - msg->Get(0, deviceType.InMsgs); + msg->Get(0, deviceType.InMsgs); // otherwise get the message type indices msg->Get(1, deviceType.OuMsgs); return AddDeviceType(taskName, deviceType); } +//============================================================================== +/* + DataDevice - Load this task with Device info. This is the main function to + populate the database. Device records may be sent in several such messages, + although it is standard to send them all in one messaeg. +*/ +//============================================================================== unsigned SBase::DataDevice(PMsg_p *msg) { // Post(715,int2str(Urank),int2str(msg->Src()),msg->Zname(0)); - string taskName = msg->Zname(0); + string taskName = msg->Zname(0); // as usual, first static field is the task if (taskName == "") { Post(705, int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } TaskData_t task; - if (GetTask(taskName, task)) + if (GetTask(taskName, task)) // does the task exist? { - Post(708,"records",taskName,int2str(Urank)); + Post(708,"records",taskName,int2str(Urank)); // no. Abort. return AddressBook::ERR_TASK_NOT_FOUND; } - string devTypName = msg->Zname(2); + string devTypName = msg->Zname(2); // device type name is in the third field DTypeIdx devIdx = 0; - while ((task.DeviceTypes[devIdx].Name != devTypName) && ++devIdx < task.DeviceTypes.size()); - if (devIdx >= task.DeviceTypes.size()) + // search through loaded task info for the device type + while ((task.DeviceTypes[devIdx].Name != devTypName) && + (++devIdx < task.DeviceTypes.size())); + if (devIdx >= task.DeviceTypes.size()) // this device type was never loaded { - Post(718,taskName,int2str(Urank),devTypName); + Post(718,taskName,int2str(Urank),devTypName); // so error. return AddressBook::ERR_INVALID_DEVTYPE; } - vector devNames; + vector devNames; // names and addresses will be in packed vectors vector devAddrs; - int numSupers = 1; // get method expects a *reference* to the count - SymAddr_t* devSuper = msg->Get(3,numSupers); + int numSupers = 1; // get method expects a *reference* to the count + // supervisor of this device is in the fourth dynamic field + SymAddr_t* devSuper = msg->Get(3,numSupers); if (!devSuper) { // no supervisor found @@ -527,30 +652,34 @@ unsigned SBase::DataDevice(PMsg_p *msg) return ERR_INVALID_SUPERVISOR; } vector devAttrs; - msg->GetX(0, devNames); - msg->Get(1, devAddrs); - if (devNames.size() != devAddrs.size()) + msg->GetX(0, devNames); // grab device names and addresses out of the first + msg->Get(1, devAddrs); // and second dynamic fields. + if (devNames.size() != devAddrs.size()) // mismatching counts is an error { Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAddrs.size()),"addresse"); return ERR_DEVICE_DATA_MISMATCH; } - msg->Get(2, devAttrs); - if (devNames.size() != devAttrs.size()) + msg->Get(2, devAttrs); // get the device attributes + // for which there should be one for each device + if (devNames.size() != devAttrs.size()) { Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAttrs.size()),"attribute"); return ERR_DEVICE_DATA_MISMATCH; } unsigned err = AddressBook::SUCCESS; - for (unsigned d = 0; d < devNames.size(); d++) + // everything checks out. Build device objects. + for (unsigned d = 0; d < devNames.size(); d++) { Record_t device(devNames[d], devAddrs[d], *devSuper, devIdx, RecordType_t(Device), - devAttrs[d]); + devAttrs[d]); + // and add them to the database if ((err = AddDevice(taskName, device)) != AddressBook::SUCCESS) { + // try to clean out the task if a device couldn't be inserted if (ClearTask(taskName) != AddressBook::SUCCESS) { Post(708, device.Name, taskName, int2str(Urank)); @@ -560,7 +689,7 @@ unsigned SBase::DataDevice(PMsg_p *msg) return err; } } - if (TaskState(taskName) < Linked) + if (TaskState(taskName) < Linked) // adding the devices auto-links the records { if ((err = TaskState(taskName,Linked))) { @@ -571,71 +700,92 @@ unsigned SBase::DataDevice(PMsg_p *msg) return AddressBook::SUCCESS; } -// ----------------------------------------------------------------------------- - +//============================================================================== +/* + DataDeviceExternal - Load this task with any devices having External + connections. Not implemented for now, awaiting definition of Externals. +*/ +//============================================================================== unsigned SBase::DataDeviceExternal(PMsg_p *msg) { return 0; } -// ----------------------------------------------------------------------------- +//============================================================================== +/* + DataExternal - Load this task with External device info. Not implemented for + now. +*/ +//============================================================================== unsigned SBase::DataExternal(PMsg_p *msg) { return 0; } -// ----------------------------------------------------------------------------- +//============================================================================== +/* + DataSupervisor - Load this task with Supervisor information. Supervisors have + both an address and an MPI rank. +*/ +//============================================================================== unsigned SBase::DataSupervisor(PMsg_p *msg) { // Post(715,int2str(Urank),int2str(msg->Src()),msg->Zname(0)); - string taskName = msg->Zname(0); - if (taskName == "") + string taskName = msg->Zname(0); // first static field is the task + if (taskName == "") // abort if there was no task name { Post(705, int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } TaskData_t task; - if (GetTask(taskName, task)) + if (GetTask(taskName, task)) // also abort if the task wasn't loaded { Post(708,"records",taskName,int2str(Urank)); return AddressBook::ERR_TASK_NOT_FOUND; } - string devTypName = msg->Zname(2); + string devTypName = msg->Zname(2); // third static field is the Supervisor type DTypeIdx devIdx = 0; - while ((task.DeviceTypes[devIdx].Name != devTypName) && ++devIdx < task.DeviceTypes.size()); - if (devIdx >= task.DeviceTypes.size()) + // Supervisors should exist in the database as a 'normal' device type + while ((task.DeviceTypes[devIdx].Name != devTypName) && + (++devIdx < task.DeviceTypes.size())); + if (devIdx >= task.DeviceTypes.size()) // nonexistent Supervisor type { Post(718,taskName,int2str(Urank),devTypName); return AddressBook::ERR_INVALID_DEVTYPE; } - vector devNames;; - vector devAddrs; + vector devNames; // containers for all the Supervisors in + vector devAddrs; // the message vector devRanks; vector devAttrs; - msg->GetX(0, devNames); + msg->GetX(0, devNames); // first dynamic field is the name // msg->Get(1, devData); - msg->Get(1, devAddrs); - if (devNames.size() != devAddrs.size()) + msg->Get(1, devAddrs); // second is the (POETS) address + if (devNames.size() != devAddrs.size()) // name-address counts must match { Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAddrs.size()),"address"); return ERR_DEVICE_DATA_MISMATCH; } - msg->Get(2, devAttrs); - if (devNames.size() != devAttrs.size()) + msg->Get(2, devAttrs); // third is the Supervisor's attributes + if (devNames.size() != devAttrs.size()) // which also has to match counts. { Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devAttrs.size()),"attribute"); return ERR_DEVICE_DATA_MISMATCH; } - msg->Get(3, devRanks); + // and fourth is the MPI rank. FIXME: this only works with a single comm. + // If supervisors are on multiple comms we will need to have some way of + // identifying the comm as well. + msg->Get(3, devRanks); if (devNames.size() != devRanks.size()) { Post(716,int2str(Urank),uint2str(devNames.size()),uint2str(devRanks.size()),"rank"); return ERR_DEVICE_DATA_MISMATCH; - } + } + // if everything was OK, load the supervisors in for (unsigned d = 0; d < devNames.size(); d++) { + // by building a record for each one Record_t device(devNames[d], devAddrs[d], static_cast(devRanks[d]), @@ -643,6 +793,7 @@ unsigned SBase::DataSupervisor(PMsg_p *msg) RecordType_t(Supervisor), devAttrs[d]); unsigned err = AddressBook::SUCCESS; + // and adding it to the database if ((err = AddDevice(taskName, device)) != AddressBook::SUCCESS) { /* @@ -660,209 +811,276 @@ unsigned SBase::DataSupervisor(PMsg_p *msg) return AddressBook::SUCCESS; } -// ----------------------------------------------------------------------------- - +//============================================================================== +/* + DumpAll - Output a comprehensive dump of all tasks loaded to this SBase, + including device info. +*/ +//============================================================================== unsigned SBase::DumpAll(PMsg_p *msg, unsigned comm) { - // filename must be treated as an absolute literal, because tasks could in - // general have different paths, therefore there is no way to infer what - // additional path string might be appended to the file name give. + /* Second static name string is the file name to dump to. The first static + string should be empty. We use the second one so that the first field is + used in a consistent way - for a task name. Filename must be treated as an + absolute literal, because tasks could in + general have different paths, therefore there is no way to infer what + additional path string might be appended to the file name given. + */ string filename = msg->Zname(1); FILE* dumpFile; - if (filename.empty()) dumpFile = stdout; + if (filename.empty()) dumpFile = stdout; // empty filename => dump to console else dumpFile = fopen(filename.c_str(), "a"); vector tasks; unsigned err = AddressBook::SUCCESS; - if ((err = ListTask(tasks))) + if ((err = ListTask(tasks))) // get all the tasks { - Post(709, int2str(Urank)); + Post(710, "Dump", int2str(Urank)); // error: no tasks were found if (dumpFile != stdout) fclose(dumpFile); - return err; + return AddressBook::ERR_NONFATAL; // so we don't need to do anything } - WALKVECTOR(string, tasks, task) Dump(dumpFile, *task); + WALKVECTOR(string, tasks, task) Dump(dumpFile, *task); // dump it all out return err; } +//============================================================================== +/* + DumpAll - Output only task summary info to a file. +*/ +//============================================================================== unsigned SBase::DumpSummary(PMsg_p *msg, unsigned comm) { - string filename = msg->Zname(1); + string filename = msg->Zname(1); // second static string - file to dump to FILE* dumpFile; - if (filename.empty()) dumpFile = stdout; + if (filename.empty()) dumpFile = stdout; // no name: dump to console else dumpFile = fopen(filename.c_str(), "a"); - Dump(dumpFile); - if (dumpFile != stdout) fclose(dumpFile); + Dump(dumpFile); // dump without a task emits a summary + if (dumpFile != stdout) fclose(dumpFile); return AddressBook::SUCCESS; } +//============================================================================== +/* + DumpTask - Output complete information about a given task to a file. +*/ +//============================================================================== unsigned SBase::DumpTask(PMsg_p *msg, unsigned comm) { - string taskName = msg->Zname(0); - if (taskName.empty()) + string taskName = msg->Zname(0); // first static string is the task to dump + if (taskName.empty()) // no name is an obvious error { - Post(710, "Dump", int2str(Urank)); + Post(710, "Dump", int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } - string filename = msg->Zname(1); + string filename = msg->Zname(1); // second static string is the file to dump to FILE* dumpFile; - if (filename.empty()) dumpFile = stdout; + if (filename.empty()) dumpFile = stdout; // if file is empty dump to console else dumpFile = fopen(filename.c_str(), "a"); - Dump(dumpFile, taskName); + Dump(dumpFile, taskName); // only dump info about this task if (dumpFile != stdout) fclose(dumpFile); return AddressBook::SUCCESS; } +//============================================================================== +/* + QueryAttr - Get all device name-address pairs where the device has the + attribute specified. Currently, the attribute is asked for by name. The + name can be a vector, in which case the query matches devices with any of + the matched names (OR matching). A bit-field representation of an attribute + (rather than a string) would also allow AND matching (extension for the + future?). + + The Query group of commands to SBase will be an external request for device + information. We do not usually expect such commands to come from internal + Orchestrator processes, although it's conceivable a Supervisor device could + ask for an internal query (which should be fulfilled locally by the SBase + of the Mothership to which the supervisor belongs). All Queries will generate + a reply to the querying process. This reply will either be a device list + (of some form) a NOTFOUND response (no devices matched the query criteria) or + a TASKNOTFOUND response (the task being queried doesn't exist in the database) +*/ +//============================================================================== unsigned SBase::QueryAttr(PMsg_p *msg, unsigned comm) { - string taskName = msg->Zname(0); - if (taskName.empty()) + string taskName = msg->Zname(0); // first static string is the task to query + if (taskName.empty()) // no task is an obvious error { Post(710, "QueryByAttribute", int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } unsigned err = SUCCESS; - vector attrs; - msg->GetX(2,attrs); - int srcProc = msg->Src(); + // query asks for attributes by name (should this be by attribute ID for + // compactness?) + vector attrs; + msg->GetX(2,attrs); // third dynamic field is the attribute set + int srcProc = msg->Src(); // need the querying source to send a reply msg->comm = Comms[comm]; - msg->Src(msg->Tgt()); + msg->Src(msg->Tgt()); // build the reply message msg->L(1,Q::RPLY); vector devAddrs; vector devNames; vector devAttrs; - WALKVECTOR(string, attrs, a) + // now grab all the devices having one or more of the attributes asked for + WALKVECTOR(string, attrs, a) { const RecordVect_t* devRecords; if ((err = FindByAttribute(taskName, *a, devRecords))) { + // filter out nonexistent task/nonexistent device queries. An + // ERR_INVALID_MAP indicates the task has a dirty device table, and + // should be rebuilt before asking for devices. if (err == ERR_TASK_NOT_FOUND || err == ERR_INVALID_MAP) { - msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); - msg->Send(srcProc); + // Q::TNF => task not found; Q::NF => no matching records found + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + msg->Send(srcProc); // send not found response return SUCCESS; } - continue; + continue; // no devices with this attribute were found } - devAttrs.push_back(*a); - for (vector::const_iterator device = devRecords->begin(); device != devRecords->end(); device++) + devAttrs.push_back(*a); // send the actual attributes matched in the reply + for (vector::const_iterator device = devRecords->begin(); + device != devRecords->end(); device++) { devAddrs.push_back((*device)->Address); devNames.push_back((*device)->Name); } } - if (!devAddrs.size()) msg->L(3, Q::NF); + if (!devAddrs.size()) msg->L(3, Q::NF); // no attributes matched else { - msg->Put(0,&devAddrs); - msg->PutX(1,&devNames); + msg->Put(0,&devAddrs); // otherwise reply with the matching + msg->PutX(1,&devNames); // devices' name-address pairs msg->PutX(2,&devAttrs); } msg->Send(srcProc); return SUCCESS; } -// Gets all devices for a task. We return every device in a single PMsg_p. This could get -// VERY big, but since there are no certainties the receiving device knows how many devices -// it's expecting, chunking the reply into bite-sized messages makes it impossible for the -// receiver to know when it can stop receiving. An alternative would be to insert the -// device count in the message, along with possibly a sequence number, but for a first -// revision this is just adding complication for possibly little benefit. To be tested. +//============================================================================== +/* + QueryDevIAll - Gets all devices for a task. We return every device in a single + PMsg_p. This could get VERY big, but since there are no certainties the + receiving device knows how many devices it's expecting, chunking the reply + into bite-sized messages makes it impossible for the receiver to know when it + can stop receiving. An alternative would be to insert the device count in the + message, along with possibly a sequence number, but for a first revision this + is just adding complication for possibly little benefit. To be tested. +*/ +//============================================================================== unsigned SBase::QueryDevIAll(PMsg_p *msg, unsigned comm) { - string taskName = msg->Zname(0); - if (taskName.empty()) + string taskName = msg->Zname(0); // as usual, first static field is the task + if (taskName.empty()) // also as usual, no task is an error. { Post(710, "QueryAllDevices", int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } unsigned err = SUCCESS; - int srcProc = msg->Src(); + int srcProc = msg->Src(); // set up the reply to the querying process msg->comm = Comms[comm]; msg->Src(msg->Tgt()); msg->L(1,Q::RPLY); vector devAddrs; vector devNames; const vector* devRecords; - if ((err = GetDevices(taskName, devRecords))) + if ((err = GetDevices(taskName, devRecords))) // grab all the devices { - msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); + // as long as any exist + msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); msg->Send(srcProc); return SUCCESS; } - for (vector::const_iterator device = devRecords->begin(); device != devRecords->end(); device++) + // reply message gives all the device name-address pairs. + for (vector::const_iterator device = devRecords->begin(); + device != devRecords->end(); device++) { devAddrs.push_back(device->Address); devNames.push_back(device->Name); - } - if (!devAddrs.size()) msg->L(3, Q::NF); + } + // no devices. Should have been caught above, but just in case... + if (!devAddrs.size()) msg->L(3, Q::NF); else { - msg->Put(0,&devAddrs); - msg->PutX(1,&devNames); + msg->Put(0,&devAddrs); // addresses and names are both + msg->PutX(1,&devNames); // packed as vectors. } msg->Send(srcProc); return SUCCESS; } -// Query device by name. A question remains here - how do we deal with the queries -// asking for a group? +//============================================================================== +/* + QueryDevIByName - This gets device addresses from a device name. We handle + both the case of a simple query for a device address given a name, and the + more complex cases where we want all devices in the same group as a named + device, or where we want the supervisor of the named device. The latter will + return only the address, in the first dynamic field, the former will return a + vector of device address-name pairs, in the first and second dynamic fields. +*/ +//============================================================================== unsigned SBase::QueryDevIByName(PMsg_p *msg, unsigned comm) { + // check that the key type is valid - if not the query is unrecognised. if (!((msg->L(3) == Q::NGRP) || (msg->L(3) == Q::NM) || (msg->L(3) == Q::NSUP))) { Post(702,uint2str(msg->Key()),int2str(Urank)); return ERR_NONFATAL; } - string taskName = msg->Zname(0); - if (taskName.empty()) + string taskName = msg->Zname(0); // first static field is the task name + if (taskName.empty()) // which, as usual, must exist. { Post(710, "QueryDeviceByName", int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } - string deviceName = msg->Zname(1); - if (deviceName.empty()) + string deviceName = msg->Zname(1); // second static field is the device name + if (deviceName.empty()) // which also must exist or it is an error. { Post(731, int2str(Urank)); return ERR_INVALID_DEVICE; } unsigned err = SUCCESS; - int srcProc = msg->Src(); + int srcProc = msg->Src(); // query is valid - set up a reply. msg->comm = Comms[comm]; msg->Src(msg->Tgt()); msg->L(1,Q::RPLY); const Record_t* device; - if ((err = FindDevice(taskName, deviceName, device))) + if ((err = FindDevice(taskName, deviceName, device))) // get the base device { + // if the device or task doesn't exist, we can reply immediately msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); msg->Send(srcProc); return SUCCESS; } - if (msg->L(3) == Q::NGRP) + if (msg->L(3) == Q::NGRP) // asking for all devices in the same group. { vector devAddrs; vector devNames; const RecordVect_t* devRecords; - if ((err = FindBySuper(taskName, device->Supervisor, devRecords))) + // same group => devices share a supervisor. Look up by supervisor of + // named device. + if ((err = FindBySuper(taskName, device->Supervisor, devRecords)) || + (devRecords->size() == 0)) { - // given that at least the lookup device must belong to its Supervisor, finding - // no devices attached to the Supervisor in question would be a serious error! - Post(732,int2str(Urank),uint2str(device->Supervisor),device->Name,err == ERR_TASK_NOT_FOUND ? "task" : "devices"); + // given that at least the lookup device must belong to its + // Supervisor, finding no devices attached to the Supervisor in + // question would be a serious error! + Post(732,int2str(Urank),uint2str(device->Supervisor),device->Name, + err == ERR_TASK_NOT_FOUND ? "task" : "devices"); msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); msg->Send(srcProc); return ERR_DEVICE_DATA_MISMATCH; } - for (vector::const_iterator deviceIt = devRecords->begin(); deviceIt != devRecords->end(); deviceIt++) + // bundle up all the device address-name pairs and send them as the reply. + for (vector::const_iterator deviceIt = devRecords->begin(); + deviceIt != devRecords->end(); deviceIt++) { devAddrs.push_back((*deviceIt)->Address); devNames.push_back((*deviceIt)->Name); } - if (!devAddrs.size()) msg->L(3, Q::NF); - else - { - msg->Put(0,&devAddrs); - msg->PutX(1,&devNames); - } + msg->Put(0,&devAddrs); + msg->PutX(1,&devNames); } - else + else // expecting only to respond with an address - either a supervisor or device { // these have to be copied into a temporary (and then recopied into the message) // because Put expects a type match, while the iterator returns constant records, @@ -877,20 +1095,33 @@ unsigned SBase::QueryDevIByName(PMsg_p *msg, unsigned comm) return SUCCESS; } +//============================================================================== +/* + QueryDevIByID - Gets device names (and possibly addresses from a device + address. This mirrors (almost) the functionality of QueryDevIByName. The only + difference is that if a Supervisor is being queried, only the Supervisor ID + is returned (it makes no sense to ask for a Supervisor name, since from the + application POV, there is only one Supervisor device, so its name is + automatically known). +*/ +//============================================================================== unsigned SBase::QueryDevIByID(PMsg_p *msg, unsigned comm) { + // just like the ByName case, validate the query type if (!((msg->L(3) == Q::IGRP) || (msg->L(3) == Q::ID) || (msg->L(3) == Q::ISUP))) { Post(702,uint2str(msg->Key()),int2str(Urank)); return ERR_NONFATAL; } - string taskName = msg->Zname(0); - if (taskName.empty()) + string taskName = msg->Zname(0); // as always, first static field is the task + if (taskName.empty()) // and an empty task is an error. { Post(710, "QueryDeviceByID", int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } int devCount = 0; + // first dynamic field is the device address to query on. This must exist, + // so if the count of values in the first field is 0, it's an error. SymAddr_t* deviceAddr = msg->Get(0,devCount); if (!devCount) { @@ -898,47 +1129,53 @@ unsigned SBase::QueryDevIByID(PMsg_p *msg, unsigned comm) return ERR_INVALID_DEVICE; } unsigned err = SUCCESS; - int srcProc = msg->Src(); + int srcProc = msg->Src(); // query is valid, so set up the reply. msg->comm = Comms[comm]; msg->Src(msg->Tgt()); msg->L(1,Q::RPLY); const Record_t* device; + // find the device being queried by ID. if ((err = FindDevice(taskName, *deviceAddr, device))) { + // no such device, or no such task. msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); msg->Send(srcProc); return SUCCESS; } - if (msg->L(3) == Q::IGRP) + if (msg->L(3) == Q::IGRP) // we want all devices in the same group { vector devAddrs; vector devNames; const RecordVect_t* devRecords; - if ((err = FindBySuper(taskName, device->Supervisor, devRecords))) + // just like the ByName case, look up by supervisor of the query device + if ((err = FindBySuper(taskName, device->Supervisor, devRecords)) || + (devRecords->size() == 0)) { - // given that at least the lookup device must belong to its Supervisor, finding - // no devices attached to the Supervisor in question would be a serious error! - Post(732,int2str(Urank),uint2str(device->Supervisor),device->Name,err == ERR_TASK_NOT_FOUND ? "task" : "devices"); + // given that at least the lookup device must belong to its Supervisor, + // finding no devices attached to the Supervisor in question would be + // a serious error! + Post(732,int2str(Urank),uint2str(device->Supervisor),device->Name, + err == ERR_TASK_NOT_FOUND ? "task" : "devices"); msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); msg->Send(srcProc); return ERR_DEVICE_DATA_MISMATCH; } - for (vector::const_iterator deviceIt = devRecords->begin(); deviceIt != devRecords->end(); deviceIt++) + // and in the same way, as long as devices are found, return their + // address-name pairs (which should include the queried device) + for (vector::const_iterator deviceIt = devRecords->begin(); + deviceIt != devRecords->end(); deviceIt++) { devAddrs.push_back((*deviceIt)->Address); devNames.push_back((*deviceIt)->Name); } - if (!devAddrs.size()) msg->L(3, Q::NF); - else - { - msg->Put(0,&devAddrs); - msg->PutX(1,&devNames); - } + msg->Put(0,&devAddrs); + msg->PutX(1,&devNames); } else { + // for a simple device, return the name in the first static field msg->Zname(1,device->Name); - if (msg->L(3) == Q::ISUP) + if (msg->L(3) == Q::ISUP) // supervisors return their address { SymAddr_t nonConstSuperAddr = device->Supervisor; msg->Put(1,&nonConstSuperAddr); @@ -948,25 +1185,36 @@ unsigned SBase::QueryDevIByID(PMsg_p *msg, unsigned comm) return SUCCESS; } +//============================================================================== +/* + QueryDevT - Gets device name-address pairs with a given type. There are 3 + flavours of this query: NM - matches on the type, IN - matches on all devices + sharing an input with the same message type, OUT - matches on all devices + sharing an output with the same message type. +*/ +//============================================================================== unsigned SBase::QueryDevT(PMsg_p *msg, unsigned comm) { + // validate the query flavour if (!((msg->L(3) == Q::IN) || (msg->L(3) == Q::NM) || (msg->L(3) == Q::OUT))) { Post(702,uint2str(msg->Key()),int2str(Urank)); return ERR_NONFATAL; } - string taskName = msg->Zname(0); - if (taskName.empty()) + string taskName = msg->Zname(0); // first static field is the task name + if (taskName.empty()) // no task name is an error as always { Post(710, "QueryDeviceByName", int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } - string lookupType = msg->Zname(2); + string lookupType = msg->Zname(2); // third static field is the type name if (lookupType.empty()) { + // no type name is an error - different error class depending on the + // query flavour if (msg->L(3) == Q::NM) { - Post(706, "find",taskName,int2str(Urank)); + Post(706,"find",taskName,int2str(Urank)); return ERR_INVALID_DEVTYPE; } else @@ -977,28 +1225,33 @@ unsigned SBase::QueryDevT(PMsg_p *msg, unsigned comm) } unsigned err = SUCCESS; - int srcProc = msg->Src(); + int srcProc = msg->Src(); // query is valid; set up the reply msg->comm = Comms[comm]; msg->Src(msg->Tgt()); msg->L(1,Q::RPLY); vector devAddrs; vector devNames; const RecordVect_t* devRecords; + // do the lookup for the 3 different query flavours: whichever is + // specified, we will return name-address pairs. if (msg->L(3) == Q::NM) err = FindByType(taskName,lookupType,devRecords); else if (msg->L(3) == Q::IN) err = FindByInMsg(taskName,lookupType,devRecords); else err = FindByOuMsg(taskName,lookupType,devRecords); if (err) { + // no such devices found or no such task found: immediate reply msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); msg->Send(srcProc); return SUCCESS; } - for (vector::const_iterator device = devRecords->begin(); device != devRecords->end(); device++) + // otherwise pack the reply vectors with addresses and names + for (vector::const_iterator device = devRecords->begin(); + device != devRecords->end(); device++) { devAddrs.push_back((*device)->Address); devNames.push_back((*device)->Name); } - if (!devAddrs.size()) msg->L(3, Q::NF); + if (!devAddrs.size()) msg->L(3, Q::NF); // should have been caught above else { msg->Put(0,&devAddrs); @@ -1009,72 +1262,97 @@ unsigned SBase::QueryDevT(PMsg_p *msg, unsigned comm) } +//============================================================================== +/* + QueryExtn - Gets all external device name-address pairs. An External is a + virtual device not hosted in the POETS system but that can interact with + other devices as if it were. Since we currently don't have any support for + externals in the rest of the system, for the moment this query won't do + very much (and it can't be practicably tested), but the machinery looks + just like a QueryDevIAll so it can be implemented in principle. +*/ +//============================================================================== unsigned SBase::QueryExtn(PMsg_p *msg, unsigned comm) { - string taskName = msg->Zname(0); - if (taskName.empty()) + string taskName = msg->Zname(0); // as usual, first static field is the task + if (taskName.empty()) // also as usual, no task is an error. { Post(710, "QueryAllExternals", int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } unsigned err = SUCCESS; - int srcProc = msg->Src(); + int srcProc = msg->Src(); // set up the reply to the querying process msg->comm = Comms[comm]; msg->Src(msg->Tgt()); msg->L(1,Q::RPLY); vector devAddrs; vector devNames; const vector* devRecords; - if ((err = GetExternals(taskName, devRecords))) + if ((err = GetExternals(taskName, devRecords))) // grab all the externals { + // as long as any exist msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); msg->Send(srcProc); return SUCCESS; } - for (vector::const_iterator device = devRecords->begin(); device != devRecords->end(); device++) + // reply message gives all the external name-address pairs. + for (vector::const_iterator device = devRecords->begin(); + device != devRecords->end(); device++) { devAddrs.push_back(device->Address); devNames.push_back(device->Name); - } + } + // no externals. Should have been caught above, but just in case... if (!devAddrs.size()) msg->L(3, Q::NF); else { - msg->Put(0,&devAddrs); - msg->PutX(1,&devNames); + msg->Put(0,&devAddrs); // addresses and names are both + msg->PutX(1,&devNames); // packed as vectors. } msg->Send(srcProc); return SUCCESS; } -// ask for a list of tasks on this SBase. Can't fail: either we have tasks or not -// so we just return a list if we have them, a not found message if not. +//============================================================================== +/* + QueryList - Ask for a list of tasks on this SBase. Can't fail: either we have + tasks or not so we just return a list if we have them, a not found message if + not. +*/ +//============================================================================== unsigned SBase::QueryList(PMsg_p *msg, unsigned comm) { unsigned err = SUCCESS; - int srcProc = msg->Src(); + int srcProc = msg->Src(); // query is automatically valid; set up a reply msg->comm = Comms[comm]; msg->Src(msg->Tgt()); msg->L(1,Q::RPLY); vector taskNames; + // if the list produces no tasks, return with task not found if ((err = ListTask(taskNames)) || (taskNames.size() == 0)) msg->L(3,Q::TNF); - else msg->PutX(1,&taskNames); + else msg->PutX(1,&taskNames); // otherwise return the vector of task names msg->Send(srcProc); return SUCCESS; } -// Get all supervisors for a task. We have one more list in addition to the -// address and the name: the list of ranks, because the requesting process -// may wish to identify a Supervisor by MPI rank rather than POETS address. +//============================================================================== +/* + QuerySupv - Get all supervisors for a task. We return one more list in + addition to the address and the name: the list of ranks, because the + requesting process may wish to identify a Supervisor by MPI rank rather than + POETS address. Otherwise this looks like the other 'all'-type queries. +*/ +//============================================================================== unsigned SBase::QuerySupv(PMsg_p *msg, unsigned comm) { - string taskName = msg->Zname(0); - if (taskName.empty()) + string taskName = msg->Zname(0); // as usual, first static field is the task + if (taskName.empty()) // also as usual, no task is an error. { Post(710, "QueryAllSupervisors", int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } unsigned err = SUCCESS; - int srcProc = msg->Src(); + int srcProc = msg->Src(); // set up the reply to the querying process msg->comm = Comms[comm]; msg->Src(msg->Tgt()); msg->L(1,Q::RPLY); @@ -1082,53 +1360,67 @@ unsigned SBase::QuerySupv(PMsg_p *msg, unsigned comm) vector devNames; vector devRanks; const vector* devRecords; - if ((err = GetSupervisors(taskName, devRecords))) + if ((err = GetSupervisors(taskName, devRecords))) // grab all the supervisors { + // as long as any exist msg->L(3, err == ERR_TASK_NOT_FOUND ? Q::TNF : Q::NF); msg->Send(srcProc); return SUCCESS; } - for (vector::const_iterator device = devRecords->begin(); device != devRecords->end(); device++) + // reply message gives name-address-rank triplets for each Supervisor + for (vector::const_iterator device = devRecords->begin(); + device != devRecords->end(); device++) { devAddrs.push_back(device->Address); devNames.push_back(device->Name); devRanks.push_back(device->Rank); - } + } + // no supervisors. Should have been caught above, but just in case... if (!devAddrs.size()) msg->L(3, Q::NF); else { - msg->Put(0,&devAddrs); - msg->PutX(1,&devNames); - msg->Put(2,&devRanks); + msg->Put(0,&devAddrs); // addresses, + msg->PutX(1,&devNames); // names, and + msg->Put(2,&devRanks); // ranks are all packed as vectors } msg->Send(srcProc); return SUCCESS; } -// A task query might end up being some set of requests with further internal details -// in future, but for the moment we simply return summary task info. +//============================================================================== +/* + QueryTask - A task query might end up being some set of requests with further + internal details in future, but for the moment this simply returns summary + task info, like a dump, but to a remote querying process. +*/ +//============================================================================== unsigned SBase::QueryTask(PMsg_p *msg, unsigned comm) { - string taskName = msg->Zname(0); - if (taskName.empty()) + string taskName = msg->Zname(0); // first static field is the task, as always + if (taskName.empty()) // and no task name is an error. { Post(710, "QueryTask", int2str(Urank)); return AddressBook::ERR_INVALID_TASK; } unsigned err = SUCCESS; - int srcProc = msg->Src(); + int srcProc = msg->Src(); // set up the reply msg->comm = Comms[comm]; msg->Src(msg->Tgt()); msg->L(1,Q::RPLY); TaskData_t task; - if ((err = GetTask(taskName, task))) + if ((err = GetTask(taskName, task))) // get the task info { - msg->L(3,Q::TNF); + msg->L(3,Q::TNF); // there is no such task msg->Send(srcProc); return SUCCESS; } - switch (task.State) - { + // turn the state into a string for easier reading at the querying process + msg->Zname(1,State2Str(task.State)); + if (msg->Zname(1) == "Unknown") + Post(734,taskName,int2str(Urank)); // some strange unknown task state + /* + switch (task.State) + { case Loaded: msg->Zname(1,"Loaded"); break; @@ -1151,15 +1443,16 @@ unsigned SBase::QueryTask(PMsg_p *msg, unsigned comm) msg->Zname(1,"Finished"); break; default: - Post(734,taskName,int2str(Urank)); + Post(734,taskName,int2str(Urank)); msg->Zname(1,"Unknown"); } - msg->Zname(2,task.Path); - msg->Zname(3,task.XML); + */ + msg->Zname(2,task.Path); // dump all other available info + msg->Zname(3,task.XML); // into the reply message msg->PutX(0,&task.MessageTypes); msg->PutX(1,&task.AttributeTypes); - vector counts; - counts.push_back(task.DeviceCount); + vector counts; // put device counts in a vector + counts.push_back(task.DeviceCount); // (just so we have fewer fields) counts.push_back(task.ExternalCount); counts.push_back(task.SupervisorCount); msg->Put(2,&counts); @@ -1167,6 +1460,20 @@ unsigned SBase::QueryTask(PMsg_p *msg, unsigned comm) return SUCCESS; } +//============================================================================== +/* + The Send group of commands. In general, these forward on pre-packed messages + containing POETS packets to some subset of devices in a POETS system. These + are all virtual in SBase, because what a given process actually does with a + Send depends on what type of process it is. A Nameserver, for example, needs + to detect which Supervisors to talk to and relay the message on to them. A + Supervisor needs either to unpack the message and send it over HostLink, or + identify where to forward the message, if it doesn't have the devices being + targetted. The functions here are not pure-virtual, though, because one can + conceive of SBase processes which simply don't implement Send functionality. + So we provide the not-implemented stubs here. +*/ +//============================================================================== unsigned SBase::SendAttr(PMsg_p *msg, unsigned comm) { return 0; @@ -1202,6 +1509,11 @@ unsigned SBase::SendSupv(PMsg_p *msg, unsigned comm) return 0; } +//============================================================================== +/* + State2Str - a static method to transform a state enum into a readable string +*/ +//============================================================================== string SBase::State2Str(TaskState_t state) { switch (state) @@ -1224,6 +1536,11 @@ string SBase::State2Str(TaskState_t state) return "Unknown"; } +//============================================================================== +/* + Str2State - a static method to transform a state string into a state enum +*/ +//============================================================================== TaskState_t SBase::Str2State(const string& state) { diff --git a/Source/NameServer/SBase.h b/Source/NameServer/SBase.h index d98cffc0..843898a9 100644 --- a/Source/NameServer/SBase.h +++ b/Source/NameServer/SBase.h @@ -6,7 +6,24 @@ using namespace AddressBookNS; //============================================================================== - +/* + +SBase is the common abstraction layer to provide name services to the +Orchestrator. Its main function is to service Q::NAME messages coming from +other processes in the system. The Q::NAME message space is itself +divided into Q::SEND (forward to devices), Q::QRY (ask name services for device +into), Q::RPLY (send a response to a query to some other process), Q::DATA +(load NameServer data into the database), Q::CFG (configure Motherships at +startup of a task), Q::CMDC (miscellaneous low-level control functions) and +Q::DUMP (output NameServer data to a file or on-screen) + +SBase is in fact a wrapper for 2 classes, CommonBase (containing the MPI +messaging layer and all its associated machinery) and AddressBook (which +provides the Name database and query functions. As such it derives from +both classes. Essentially all the methods of SBase deal with responding +to queries or building the database. +*/ +//------------------------------------------------------------------------------ class SBase : public CommonBase, public AddressBook { @@ -16,6 +33,7 @@ virtual ~ SBase(); inline virtual void Dump(FILE * f = stdout, string s = "") {AddressBook::Dump(f, s);}; +// top-level message dispatchers that handle the main subtypes of message unsigned OnCfg (PMsg_p *, unsigned); unsigned OnCmd (PMsg_p *, unsigned); unsigned OnData (PMsg_p *, unsigned); @@ -29,21 +47,28 @@ static TaskState_t Str2State (const string&); protected: -virtual unsigned Connect (string=""); +// overloaded Connect for multi-universe support +virtual unsigned Connect (string=""); +// subcommands to set up Motherships virtual unsigned ConfigBuild (PMsg_p *, unsigned); virtual unsigned ConfigDelete (PMsg_p *, unsigned); virtual unsigned ConfigDir (PMsg_p *, unsigned); virtual unsigned ConfigDistribute (PMsg_p *, unsigned); virtual unsigned ConfigRecall (PMsg_p *, unsigned); virtual unsigned ConfigState (PMsg_p *, unsigned); +// various types of dumps virtual unsigned DumpAll (PMsg_p *, unsigned); virtual unsigned DumpSummary (PMsg_p *, unsigned); virtual unsigned DumpTask (PMsg_p *, unsigned); +// queries, organised by general type (device, supervisor) virtual unsigned QueryDevIAll (PMsg_p *, unsigned); virtual unsigned QueryDevIByName (PMsg_p *, unsigned); virtual unsigned QueryDevIByID (PMsg_p *, unsigned); virtual unsigned QuerySupv (PMsg_p *, unsigned); +// forwarded messages containing POETS packets. Most of +// these should do nothing in SBase because the behaviour +// will depend upon the process class virtual unsigned SendAttr (PMsg_p *, unsigned); virtual unsigned SendDevIAll (PMsg_p *, unsigned); virtual unsigned SendDevIByName (PMsg_p *, unsigned); @@ -52,12 +77,15 @@ virtual unsigned SendDevT (PMsg_p *, unsigned); virtual unsigned SendExtn (PMsg_p *, unsigned); virtual unsigned SendSupv (PMsg_p *, unsigned); +// structures needed for CommonBase to register callbacks typedef unsigned (SBase::*pMeth)(PMsg_p*, unsigned); typedef map FnMap_t; vector FnMapx; private: +// low-level commands - usually called from a subcommand, that +// handle the nuts and bolts of the operations. unsigned CmdIntegrity (PMsg_p *); unsigned DataTask (PMsg_p *); unsigned DataDevType (PMsg_p *); diff --git a/Source/OrchestratorMessages.txt b/Source/OrchestratorMessages.txt index 5ed74f91..e885246d 100644 --- a/Source/OrchestratorMessages.txt +++ b/Source/OrchestratorMessages.txt @@ -104,7 +104,7 @@ 703(E) : "Unexpected reply to query: %s received at SBase process rank %s. Circular routing loop?" 704(E) : "Tried to load task into SBase at rank %s with no name" 705(E) : "Error while loading SBase data at rank %s: no task name" -706(E) : "Error loading device type data for task %s in SBase at rank %s: no device type name" +706(E) : "Error %sing device type data for task %s in SBase at rank %s: no device type name" 707(E) : "Error loading device %s for task %s into SBase at rank %s" 708(E) : "Error loading device %s for task %s into SBase at rank %s: no such task" 709(W) : "Unable to register supervisor %s on box %s into SBase: not found in the rank list. May need to connect" From db6112b892e698e84c3fd845f6600833ab854809 Mon Sep 17 00:00:00 2001 From: AlexRast Date: Wed, 25 Sep 2019 00:09:56 +0100 Subject: [PATCH 06/14] Mothership with full SBase added and some debug-output cleanup --- Build/gcc/Makefile | 17 +- Build/gcc/Makefile.dependencies | 19 +- Build/gcc/Makefile.executable_prerequisites | 62 +- Build/gcc/Makefile.test_prerequisites | 6 +- Generics/Cli.cpp | 1 + Generics/Msg_p.cpp | 4 +- Generics/NameBase.cpp | 6 +- Generics/pdigraph.hpp | 1 + Generics/pdigraph.tpp | 2 +- Generics/uif.cpp | 6 +- Source/Common/CommonBase.cpp | 26 +- Source/Common/CommonBase.h | 8 +- Source/Common/Debug.cpp | 7 - Source/Common/Debug.h | 10 +- .../Common/HardwareModel/HardwareAddress.cpp | 22 +- Source/Common/HardwareModel/HardwareAddress.h | 17 +- .../Common/HardwareModel/HardwareIterator.cpp | 6 +- Source/Common/HardwareModel/P_core.cpp | 8 +- Source/Common/HardwareModel/P_core.h | 1 + Source/Common/HardwareModel/P_engine.cpp | 48 +- Source/Common/HardwareModel/P_engine.h | 6 + Source/Common/OSFixes.hpp | 137 +- Source/Common/Pglobals.h | 3 +- Source/Common/ProcMap.cpp | 6 +- Source/Dummy/Dummy.cpp | 17 +- Source/Injector/InjectorMain.cpp | 4 +- Source/Launcher/Call.cpp | 118 ++ Source/Launcher/Call.h | 27 + Source/Launcher/LauncherMain.cpp | 814 ++++++++ Source/Launcher/LauncherMain.h | 57 + Source/Launcher/SSH.cpp | 56 + Source/Launcher/SSH.h | 44 + Source/LogServer/LogServer.cpp | 17 +- Source/LogServer/LogServerMain.cpp | 4 +- .../Mothership/{TMoth.cpp => Mothership.cpp} | 818 +++++--- Source/Mothership/Mothership.h | 107 + Source/Mothership/MothershipMain.cpp | 5 +- Source/Mothership/TMoth.h | 85 - Source/NameServer/AddressBook.cpp | 14 +- Source/NameServer/NameServer.cpp | 1 + Source/NameServer/NameServer.h | 1 + Source/NameServer/SBase.cpp | 51 +- Source/NameServer/SBase.h | 3 + Source/OrchBase/CMsg_p.cpp | 10 +- Source/OrchBase/Constraints.h | 1 + .../Dialect1Deployer.cpp | 2 +- .../HardwareFileNotFoundException.h | 16 - .../HardwareFileNotLoadedException.h | 17 - .../HardwareFileManager/HardwareFileParser.h | 126 -- .../HardwareFileParserException.h | 15 - .../HardwareSemanticException.h | 16 - .../HardwareSyntaxException.h | 16 - .../HardwareFileNotFoundException.h | 16 + .../HardwareFileNotLoadedException.h | 17 + .../HardwareFileReader/HardwareFileReader.h | 256 +++ .../HardwareFileReaderCore.cpp | 312 +++ .../HardwareFileReaderDialect1.cpp} | 667 ++----- .../HardwareFileReaderDialect3.cpp | 63 + .../HardwareFileReaderDialect3Engine.cpp | 329 ++++ .../HardwareFileReaderDialect3Getters.cpp | 357 ++++ .../HardwareFileReaderDialect3Items.cpp | 1736 +++++++++++++++++ .../HardwareFileReaderDialect3Sections.cpp | 608 ++++++ .../HardwareFileReaderException.h | 15 + .../HardwareSemanticException.h | 16 + .../HardwareSyntaxException.h | 16 + .../OrchBase/HardwareFileReader/Validator.cpp | 370 ++++ .../OrchBase/HardwareFileReader/Validator.h | 67 + Source/OrchBase/OrchBase.h | 4 +- Source/OrchBase/OrchBaseLink.cpp | 2 + Source/OrchBase/OrchBaseTask.cpp | 131 +- Source/OrchBase/OrchBaseTopo.cpp | 6 +- Source/OrchBase/P_builder.cpp | 182 +- Source/OrchBase/P_builder.h | 1 + Source/OrchBase/P_devtyp.cpp | 1 + Source/OrchBase/P_super.cpp | 8 +- Source/OrchBase/Placement.cpp | 57 +- Source/OrchBase/build_defs.cpp | 2 +- Source/OrchBase/build_defs.h | 3 +- Source/OrchBase/tinsel-config.h | 108 +- Source/OrchestratorMessages.txt | 22 +- Source/Parser/poetsdatatype.h | 1 + Source/Parser/psupervisordevicetype.cpp | 2 +- Source/RTCL/RTCL.cpp | 18 +- Source/RTCL/RTCLMain.cpp | 4 +- Source/Root/Root.cpp | 84 +- Source/Root/Root.h | 5 +- Source/Root/RootArgs.h | 6 + Source/Root/RootMain.cpp | 5 +- Source/Softswitch/Makefile | 6 +- Source/Softswitch/inc/poets_hardware.h | 4 +- Source/Softswitch/inc/poets_msg.h | 2 + 91 files changed, 6901 insertions(+), 1499 deletions(-) create mode 100644 Source/Launcher/Call.cpp create mode 100644 Source/Launcher/Call.h create mode 100644 Source/Launcher/LauncherMain.cpp create mode 100644 Source/Launcher/LauncherMain.h create mode 100644 Source/Launcher/SSH.cpp create mode 100644 Source/Launcher/SSH.h rename Source/Mothership/{TMoth.cpp => Mothership.cpp} (64%) create mode 100644 Source/Mothership/Mothership.h delete mode 100644 Source/Mothership/TMoth.h delete mode 100644 Source/OrchBase/HardwareFileManager/HardwareFileNotFoundException.h delete mode 100644 Source/OrchBase/HardwareFileManager/HardwareFileNotLoadedException.h delete mode 100644 Source/OrchBase/HardwareFileManager/HardwareFileParser.h delete mode 100644 Source/OrchBase/HardwareFileManager/HardwareFileParserException.h delete mode 100644 Source/OrchBase/HardwareFileManager/HardwareSemanticException.h delete mode 100644 Source/OrchBase/HardwareFileManager/HardwareSyntaxException.h create mode 100644 Source/OrchBase/HardwareFileReader/HardwareFileNotFoundException.h create mode 100644 Source/OrchBase/HardwareFileReader/HardwareFileNotLoadedException.h create mode 100644 Source/OrchBase/HardwareFileReader/HardwareFileReader.h create mode 100644 Source/OrchBase/HardwareFileReader/HardwareFileReaderCore.cpp rename Source/OrchBase/{HardwareFileManager/HardwareFileParser.cpp => HardwareFileReader/HardwareFileReaderDialect1.cpp} (50%) create mode 100644 Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3.cpp create mode 100644 Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Engine.cpp create mode 100644 Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Getters.cpp create mode 100644 Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Items.cpp create mode 100644 Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Sections.cpp create mode 100644 Source/OrchBase/HardwareFileReader/HardwareFileReaderException.h create mode 100644 Source/OrchBase/HardwareFileReader/HardwareSemanticException.h create mode 100644 Source/OrchBase/HardwareFileReader/HardwareSyntaxException.h create mode 100644 Source/OrchBase/HardwareFileReader/Validator.cpp create mode 100644 Source/OrchBase/HardwareFileReader/Validator.h create mode 100644 Source/Root/RootArgs.h diff --git a/Build/gcc/Makefile b/Build/gcc/Makefile index 2d2834c7..109635c2 100644 --- a/Build/gcc/Makefile +++ b/Build/gcc/Makefile @@ -74,10 +74,12 @@ INCLUDE_FLAGS = -I$(GENERICS_DIR) \ -I$(SOURCE_DIR)/Common \ -I$(SOURCE_DIR)/Common/HardwareModel \ -I$(SOURCE_DIR)/OrchBase \ - -I$(SOURCE_DIR)/OrchBase/HardwareFileManager \ + -I$(SOURCE_DIR)/OrchBase/HardwareFileReader \ -I$(SOURCE_DIR)/OrchBase/HardwareConfigurationDeployment \ + -I$(SOURCE_DIR)/Launcher \ -I$(SOURCE_DIR)/Injector \ -I$(SOURCE_DIR)/Parser \ + -I$(SOURCE_DIR)/Root \ -I$(SOURCE_DIR)/NameServer \ -I$(SOURCE_DIR)/Softswitch/inc \ -I$(MPICH_INC_DIR) \ @@ -90,10 +92,14 @@ INCLUDE_FLAGS = -I$(GENERICS_DIR) \ # to the hostlink and some other minor changes, though nobody has studied this # comprehensively. CXXFLAGS = $(DEPENDENCY_FLAGS) $(INCLUDE_FLAGS) \ - -std=c++11 -Wall -fPIC -pthread + -std=c++11 -Wall -fPIC -pthread -pedantic # Debug target logic. +ifdef ORCHESTRATOR_DEBUG +CXXFLAGS += -g3 -DORCHESTRATOR_DEBUG=1 +else debug: CXXFLAGS += -g3 -DORCHESTRATOR_DEBUG=1 +endif debug: all # Handy aliases for building executables, scripts, and libraries. Order doesn't @@ -101,6 +107,7 @@ debug: all ORCHESTRATE_BASENAME = orchestrate.sh ORCHESTRATE_SCRIPT = $(ROOT_DIR)/$(ORCHESTRATE_BASENAME) +LAUNCHER_EXECUTABLE = $(EXECUTABLE_DIR)/orchestrate ROOT_EXECUTABLE = $(EXECUTABLE_DIR)/root DUMMY_EXECUTABLE = $(EXECUTABLE_DIR)/dummy LOGSERVER_EXECUTABLE = $(EXECUTABLE_DIR)/logserver @@ -136,9 +143,9 @@ tests: $(ALL_TESTS) $(TEST_SCRIPT) # Linking targets (to generate executables). See # Makefile.executable_prerequisites for the prerequisites of these executables # (obviously...) -$(ROOT_EXECUTABLE) $(DUMMY_EXECUTABLE) $(LOGSERVER_EXECUTABLE) \ -$(NAMESERVER_EXECUTABLE) $(RTCL_EXECUTABLE) $(INJECTOR_EXECUTABLE) \ -$(MOTHERSHIP_DUMMY_EXECUTABLE): +$(LAUNCHER_EXECUTABLE) $(ROOT_EXECUTABLE) $(DUMMY_EXECUTABLE) \ +$(LOGSERVER_EXECUTABLE) $(NAMESERVER_EXECUTABLE) $(RTCL_EXECUTABLE) \ +$(INJECTOR_EXECUTABLE) $(MOTHERSHIP_DUMMY_EXECUTABLE): @$(shell $(MKDIR) $(EXECUTABLE_DIR)) $(CXX) -pthread -Wl,-rpath-link=$(QT_LIB_DIR) \ -L$(MPICH_LIB_DIR) -L$(QT_LIB_DIR) -L/usr/lib \ diff --git a/Build/gcc/Makefile.dependencies b/Build/gcc/Makefile.dependencies index 13418750..1299a2c4 100644 --- a/Build/gcc/Makefile.dependencies +++ b/Build/gcc/Makefile.dependencies @@ -7,12 +7,23 @@ # where you have extracted it to. # # In Mark's case, it looks like this: -ORCHESTRATOR_DEPENDENCIES_DIR ?= /local/orchestrator-common/orchestrator_dependencies-5 +ORCHESTRATOR_DEPENDENCIES_DIR ?= /local/orchestrator-common/orchestrator_dependencies-6 # If you're using the "orchestrator_dependencies" tarball, you shouldn't need # to do anything more. If you aren't, you'll need to configure the following to # match your setup however... +# Tinsel, search in this order, using secondary if none found. +#TINSEL_DEFAULT_PRIMARY = "/local/tinsel" +TINSEL_DEFAULT_PRIMARY = "/home/alex_rast/adr1r17/tinsel/tinsel-tinsel-0.6.1" +TINSEL_DEFAULT_SECONDARY = "$(ROOT_DIR)/Tinsel" +TINSEL_DIR = $(shell test -d "$(TINSEL_DEFAULT_PRIMARY)" && \ + echo "$(TINSEL_DEFAULT_PRIMARY)" || \ + echo "$(TINSEL_DEFAULT_SECONDARY)") +TINSEL_INC_DIR = $(TINSEL_DIR)/include +TINSEL_LIB_DIR = $(TINSEL_DIR)/lib +TINSEL_HOSTLINK_DIR = $(TINSEL_DIR)/hostlink + # MPI (mpich here) MPICH_DIR := $(ORCHESTRATOR_DEPENDENCIES_DIR)/mpich MPICH_LIB_DIR := $(MPICH_DIR)/lib @@ -27,12 +38,6 @@ QT_DIR = $(ORCHESTRATOR_DEPENDENCIES_DIR)/Qt5.6.3/5.6.3/gcc_64 QT_LIB_DIR = $(QT_DIR)/lib QT_INC_DIR = $(QT_DIR)/include -# Tinsel (must be out of date, for now) -TINSEL_DIR = $(ORCHESTRATOR_DEPENDENCIES_DIR)/tinsel -TINSEL_INC_DIR = $(TINSEL_DIR)/include -TINSEL_LIB_DIR = $(TINSEL_DIR)/lib -TINSEL_HOSTLINK_DIR = $(TINSEL_DIR)/hostlink - # GCC, not used for building the Orchestrator, but necessary for the # Mothership. Immediate-set, because it's used by the shell. GCC_LIB_DIR := $(ORCHESTRATOR_DEPENDENCIES_DIR)/gcc-7.3.0/lib64 diff --git a/Build/gcc/Makefile.executable_prerequisites b/Build/gcc/Makefile.executable_prerequisites index 5e486266..b8746102 100755 --- a/Build/gcc/Makefile.executable_prerequisites +++ b/Build/gcc/Makefile.executable_prerequisites @@ -42,6 +42,7 @@ HARDWARE_ADDRESS_SOURCES = $(SOURCE_DIR)/Common/HardwareModel/HardwareAddress.cp # Sources used by executables that require the hardware model. HARDWARE_MODEL_SOURCES = $(SOURCE_DIR)/Common/HardwareModel/AddressableItem.cpp \ + $(SOURCE_DIR)/Common/HardwareModel/HardwareIterator.cpp \ $(SOURCE_DIR)/Common/HardwareModel/P_engine.cpp \ $(SOURCE_DIR)/Common/HardwareModel/P_box.cpp \ $(SOURCE_DIR)/Common/HardwareModel/P_board.cpp \ @@ -62,12 +63,15 @@ HARDWARE_DEPLOYER_SOURCES = $(SOURCE_DIR)/OrchBase/HardwareConfigurationDeployme $(SOURCE_DIR)/OrchBase/HardwareConfigurationDeployment/MultiSimpleDeployer.cpp \ $(HARDWARE_MODEL_SOURCES) -# Sources used by executables that iterate over the POETS Engine. -HARDWARE_ITERATOR_SOURCES = $(SOURCE_DIR)/Common/HardwareModel/HardwareIterator.cpp \ - $(HARDWARE_MODEL_SOURCES) - # Sources used by executables that read hardware files. -HARDWARE_FILE_PARSER_SOURCES = $(SOURCE_DIR)/OrchBase/HardwareFileManager/HardwareFileParser.cpp \ +HARDWARE_FILE_READER_SOURCES = $(SOURCE_DIR)/OrchBase/HardwareFileReader/HardwareFileReaderCore.cpp \ + $(SOURCE_DIR)/OrchBase/HardwareFileReader/HardwareFileReaderDialect1.cpp \ + $(SOURCE_DIR)/OrchBase/HardwareFileReader/HardwareFileReaderDialect3.cpp \ + $(SOURCE_DIR)/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Engine.cpp \ + $(SOURCE_DIR)/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Getters.cpp \ + $(SOURCE_DIR)/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Items.cpp \ + $(SOURCE_DIR)/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Sections.cpp \ + $(SOURCE_DIR)/OrchBase/HardwareFileReader/Validator.cpp \ $(GENERICS_DIR)/dfprintf.cpp \ $(GENERICS_DIR)/flat.cpp \ $(GENERICS_DIR)/jnj.cpp \ @@ -90,6 +94,25 @@ LOGSERVER_MESSAGE_FILE_ORIGIN = $(SOURCE_DIR)/OrchestratorMessages.txt # The orchestrate execution script uses a template. ORCHESTRATE_TEMPLATE = ./Resources/orchestrate_template.sh +# The launcher consists of: +# +# - The "main" file (/Source/Launcher/LauncherMain.cpp) and its dependencies +# in /Source/Launcher. +# +# - Debug logic, in (/Source/Common/Debug.cpp). +# +# - Some Generics for command-line processing and file reading. +LAUNCHER_SOURCES = $(SOURCE_DIR)/Launcher/LauncherMain.cpp \ + $(SOURCE_DIR)/Launcher/Call.cpp \ + $(SOURCE_DIR)/Launcher/SSH.cpp \ + $(SOURCE_DIR)/Common/Debug.cpp \ + $(GENERICS_DIR)/Cli.cpp \ + $(GENERICS_DIR)/dfprintf.cpp \ + $(GENERICS_DIR)/flat.cpp \ + $(GENERICS_DIR)/jnj.cpp \ + $(GENERICS_DIR)/lex.cpp \ + $(GENERICS_DIR)/uif.cpp + # The root component consists of: # # - The "main" file (/Source/Root/RootMain.cpp) and its dependencies in @@ -114,15 +137,16 @@ ROOT_SOURCES = $(SOURCE_DIR)/Root/RootMain.cpp \ $(GENERICS_DIR)/filename.cpp \ $(GENERICS_DIR)/lex.cpp \ $(GENERICS_DIR)/rand.cpp \ + $(SOURCE_DIR)/Common/Debug.cpp \ $(SOURCE_DIR)/NameServer/Ns_el.cpp \ $(SOURCE_DIR)/NameServer/ABDefs.cpp \ $(SOURCE_DIR)/NameServer/ABRecord.cpp \ $(SOURCE_DIR)/NameServer/ABTask.cpp \ - $(SOURCE_DIR)/NameServer/AddressBook.cpp \ + $(SOURCE_DIR)/NameServer/AddressBook.cpp ROOT_SOURCES += $(TRULY_COMMON_SOURCES) -ROOT_SOURCES += $(HARDWARE_FILE_PARSER_SOURCES) -ROOT_SOURCES += $(HARDWARE_ITERATOR_SOURCES) +ROOT_SOURCES += $(HARDWARE_FILE_READER_SOURCES) +ROOT_SOURCES += $(HARDWARE_MODEL_SOURCES) # Add (almost) all of the sources from OrchBase and Parser. ROOT_SOURCES += $(wildcard $(SOURCE_DIR)/OrchBase/*.cpp) @@ -167,6 +191,10 @@ LOGSERVER_SOURCES = $(SOURCE_DIR)/LogServer/LogServerMain.cpp \ LOGSERVER_SOURCES += $(TRULY_COMMON_SOURCES) +# The logserver also needs OrchestratorMessages.txt to exist in the directory +# from which the Orchestrator is started. +LOGSERVER_MESSAGE_FILE_ORIGIN = $(SOURCE_DIR)/OrchestratorMessages.txt + # The nameserver component consists of: # # - The "main" file (/Source/NameServer/NameServerMain.cpp) and its derived @@ -179,8 +207,9 @@ LOGSERVER_SOURCES += $(TRULY_COMMON_SOURCES) # - The hardware and software address sources. # NAMESERVER_SOURCES = $(SOURCE_DIR)/NameServer/NameServerMain.cpp \ - $(SOURCE_DIR)/NameServer/NameServer.cpp - + $(SOURCE_DIR)/NameServer/NameServer.cpp \ + $(SOURCE_DIR)/Common/Debug.cpp + NAMESERVER_SOURCES += $(NAME_SERVICES_SOURCES) NAMESERVER_SOURCES += $(TRULY_COMMON_SOURCES) #NAMESERVER_SOURCES += $(SOFTWARE_ADDRESS_SOURCES) @@ -228,6 +257,8 @@ INJECTOR_SOURCES += $(TRULY_COMMON_SOURCES) # # - An interface source file from the softswitch, in /Source/Softswitch/src. # +# - The name services sources. +# # - The truly common sources. # # - Some "generics" (/Generics) sources. @@ -237,23 +268,22 @@ INJECTOR_SOURCES += $(TRULY_COMMON_SOURCES) # Hostlink sources are to be compiled separately. MOTHERSHIP_SOURCES = $(SOURCE_DIR)/Mothership/MothershipMain.cpp \ $(SOURCE_DIR)/Mothership/TaskInfo.cpp \ - $(SOURCE_DIR)/Mothership/TMoth.cpp \ + $(SOURCE_DIR)/Mothership/Mothership.cpp \ $(SOURCE_DIR)/Softswitch/src/poets_msg.cpp \ $(GENERICS_DIR)/dumpchan.cpp \ $(GENERICS_DIR)/rand.cpp \ + $(SOURCE_DIR)/Common/Debug.cpp \ $(SOURCE_DIR)/OrchBase/Bin.cpp \ $(SOURCE_DIR)/OrchBase/build_defs.cpp \ $(SOURCE_DIR)/OrchBase/CFrag.cpp \ - $(SOURCE_DIR)/OrchBase/CMsg_p.cpp \ $(SOURCE_DIR)/OrchBase/D_graph.cpp \ - $(SOURCE_DIR)/OrchBase/P_addr.cpp \ $(SOURCE_DIR)/OrchBase/P_device.cpp \ $(SOURCE_DIR)/OrchBase/P_message.cpp \ $(SOURCE_DIR)/OrchBase/P_pin.cpp \ $(SOURCE_DIR)/OrchBase/P_task.cpp \ - $(SOURCE_DIR)/Common/Debug.cpp \ $(HARDWARE_DEPLOYER_SOURCES) +MOTHERSHIP_SOURCES += $(NAME_SERVICES_SOURCES) MOTHERSHIP_SOURCES += $(TRULY_COMMON_SOURCES) # A dummy mothership is an executable to provide mothership-like functionality @@ -303,7 +333,7 @@ $(1)_OBJECTS := $(call orch_sources_to_objects, $($(1)_SOURCES)) endef $(foreach object_set,\ - ROOT INJECTOR DUMMY LOGSERVER NAMESERVER RTCL MOTHERSHIP MOTHERSHIP_DUMMY,\ + LAUNCHER ROOT INJECTOR DUMMY LOGSERVER NAMESERVER RTCL MOTHERSHIP MOTHERSHIP_DUMMY,\ $(eval $(call OBJECT_TEMPLATE,$(object_set)))) # Define executable prerequisites. RULE_TEMPLATE defines the substitutions made @@ -316,5 +346,5 @@ $($(1)_EXECUTABLE): $($(1)_OBJECTS) endef $(foreach executable_name,\ - ROOT DUMMY LOGSERVER NAMESERVER RTCL INJECTOR MOTHERSHIP MOTHERSHIP_DUMMY,\ + LAUNCHER ROOT DUMMY LOGSERVER NAMESERVER RTCL INJECTOR MOTHERSHIP MOTHERSHIP_DUMMY,\ $(eval $(call RULE_TEMPLATE,$(executable_name)))) diff --git a/Build/gcc/Makefile.test_prerequisites b/Build/gcc/Makefile.test_prerequisites index 1b624687..3552996d 100644 --- a/Build/gcc/Makefile.test_prerequisites +++ b/Build/gcc/Makefile.test_prerequisites @@ -21,9 +21,11 @@ TEST_SCRIPT_RESOURCE = ./Resources/run-tests.sh # Each test file must have its dependencies enumerated separately. The names of # these variables match the tests that they generate. The test .cpp file is # included when the dependencies are generated. +TestDialect1Reader_SOURCES = $(HARDWARE_FILE_READER_SOURCES) +TestDialect3Reader_SOURCES = $(HARDWARE_FILE_READER_SOURCES) TestHardwareAddressAndFormat_SOURCES = $(HARDWARE_ADDRESS_SOURCES) -TestHardwareFileParser_SOURCES = $(HARDWARE_FILE_PARSER_SOURCES) -TestHardwareIterator_SOURCES = $(HARDWARE_ITERATOR_SOURCES) +TestHardwareFileReader_SOURCES = $(HARDWARE_FILE_READER_SOURCES) +TestHardwareIterator_SOURCES = $(HARDWARE_MODEL_SOURCES) TestHardwareModel_SOURCES = $(HARDWARE_MODEL_SOURCES) TestSimpleDeployment_SOURCES = $(HARDWARE_DEPLOYER_SOURCES) TestSoftwareAddress_SOURCES = $(SOFTWARE_ADDRESS_SOURCES) diff --git a/Generics/Cli.cpp b/Generics/Cli.cpp index 8626c32a..2673aee4 100644 --- a/Generics/Cli.cpp +++ b/Generics/Cli.cpp @@ -57,6 +57,7 @@ for(int state=0;;) { // And walk the syntax graph... case Lex::Sy_EOF : case Lex::Sy_EOR : toktyp = t4; break; case Lex::Sy_cmnt : toktyp = t7; break; + default: break; } next = table[state][toktyp]; // Make the transition switch (next.ac) { // Post-transition (exit) actions diff --git a/Generics/Msg_p.cpp b/Generics/Msg_p.cpp index 065d0240..5ebe9730 100644 --- a/Generics/Msg_p.cpp +++ b/Generics/Msg_p.cpp @@ -278,7 +278,7 @@ void Msg_p::L(int index,byte val) // Set a subkey field { int i = int(index); -if ((i<0)||(i>=Z_FIELDS)) return; +if ((i<0)||(i>=static_cast(Z_FIELDS))) return; vm.clear(); // Stream vector is now dirty subkey[i] = val; } @@ -289,7 +289,7 @@ byte Msg_p::L(int index) // Retrieve a subkey field { int i = int(index); -if ((i<0)||(i>=Z_FIELDS)) return 0xff; +if ((i<0)||(i>=static_cast(Z_FIELDS))) return 0xff; return subkey[i]; } diff --git a/Generics/NameBase.cpp b/Generics/NameBase.cpp index a46ae94c..163bd277 100644 --- a/Generics/NameBase.cpp +++ b/Generics/NameBase.cpp @@ -3,6 +3,8 @@ #include "NameBase.h" #include "macros.h" +#include "OSFixes.hpp" + unsigned NameBase::uid = 0; map NameBase::NBmap; @@ -40,10 +42,10 @@ return n; void NameBase::Dump(FILE * fp) { fprintf(fp,"NameBase dump+++++++++++++++++++++++++++++++\n"); -fprintf(fp,"this %#08p\n",this); +fprintf(fp,"this %" PTR_FMT "\n",reinterpret_cast(this)); fprintf(fp,"Name %s\n",name.c_str()); fprintf(fp,"Id %10u(%#010x)\n",id,id); -fprintf(fp,"Parent %#08p\n",npar); +fprintf(fp,"Parent %" PTR_FMT "\n",reinterpret_cast(npar)); fprintf(fp,"Recursion trap %s\n",rtrap ? "Set" : "Unset"); fprintf(fp,"Unique id %u\n",uid); fprintf(fp,"NameBase id Name\n"); diff --git a/Generics/pdigraph.hpp b/Generics/pdigraph.hpp index 4af49d1f..8b95e25d 100644 --- a/Generics/pdigraph.hpp +++ b/Generics/pdigraph.hpp @@ -3,6 +3,7 @@ #include "rand.h" #include +#include #include #include using namespace std; diff --git a/Generics/pdigraph.tpp b/Generics/pdigraph.tpp index c70b3487..a5c84eb7 100644 --- a/Generics/pdigraph.tpp +++ b/Generics/pdigraph.tpp @@ -181,7 +181,7 @@ return true; PDIGRAPH_ void _PDIGRAPH::Dump() { -string s(35,'='); +std::string s(35,'='); fprintf(dfp,"Pdigraph topological dump %35s++++++++++++++++++++\n",s.c_str()); fprintf(dfp,"Node index (%u entries):\n",static_cast(index_n.size())); int c1 = 1; diff --git a/Generics/uif.cpp b/Generics/uif.cpp index 190faa64..a81929be 100644 --- a/Generics/uif.cpp +++ b/Generics/uif.cpp @@ -1448,7 +1448,8 @@ p->P()=0; // Disconnect parent pointer void UIF::Node::Dump(FILE * df,string s0) { fprintf(df,"\n---\n%sDumping node %p (par %p) type |%s|\n", - s0.c_str(),this,par,Notype_str[typ]); + s0.c_str(), static_cast(this), + static_cast(par), Notype_str[typ]); fprintf(df,"%sOpcode |%s|\n",s0.c_str(),Lex::Sytype_str[qop]); fprintf(df,"%sString |%s|\n",s0.c_str(),str.c_str()); fprintf(df,"%sPos %d\n",s0.c_str(),pos); @@ -1465,7 +1466,8 @@ void UIF::Node::Dumpt(FILE * fp) // Tiny inline Node dump { fprintf(fp,"%6p(%2d) %s:%s[%s]\n", - this,pos,Notype_str[typ],str.c_str(),Lex::Sytype_str[qop]); + static_cast(this),pos,Notype_str[typ], + str.c_str(),Lex::Sytype_str[qop]); } //------------------------------------------------------------------------------ diff --git a/Source/Common/CommonBase.cpp b/Source/Common/CommonBase.cpp index 5147143f..3d7ab58b 100644 --- a/Source/Common/CommonBase.cpp +++ b/Source/Common/CommonBase.cpp @@ -9,6 +9,8 @@ #include "Unrec_t.h" #include +#include "OSFixes.hpp" + #include using namespace std; typedef unsigned char byte; @@ -168,13 +170,28 @@ WALKVECTOR(FnMap_t*,FnMapx,F) fprintf(fp,"Function table for comm %d:\n", cIdx++); fprintf(fp,"Key Method\n"); WALKMAP(unsigned,pMeth,(**F),i) - fprintf(fp,"%#010x 0x%#016x\n",(*i).first,(*i).second); +{ + //fprintf(fp,"%#010x 0x%#016x\n",(*i).first,(*i).second); + fprintf(fp,"%#010x ",(*i).first); + + // Now for a horrible double type cast to get us a sensible function pointer. + // void*s are only meant to point to objects, not functions. So we get to a + // void** as a pointer to a function pointer is an object pointer. We can then + // follow this pointer to get to the void*, which we then reinterpret to get + // the function's address as a uint64_t. + fprintf(fp,"%" PTR_FMT "\n",reinterpret_cast( + *(reinterpret_cast(&((*i).second)))) + ); +} } fprintf(fp,"S00 (const static) : %s\n",S00.c_str()); fprintf(fp,"Urank : %d\n",Urank); cIdx = 0; WALKVECTOR(int,Usize,s) - fprintf(fp,"Usize (comm %lu) : %d\n",cIdx++,s); +{ + fprintf(fp,"Usize (comm %u)",cIdx++); + fprintf(fp," : %ld\n",std::distance(Usize.begin(),s)); +} fprintf(fp,"Ulen : %d\n",Ulen); fprintf(fp,"Sproc : %s\n",Sproc.c_str()); fprintf(fp,"Suser : %s\n",Suser.c_str()); @@ -354,7 +371,8 @@ void CommonBase::OnIdle() unsigned CommonBase::OnPmap(PMsg_p * Z,unsigned cIdx) { pPmap[cIdx]->Register(Z); // Load one element into the process map -if ((cIdx == 0) && (pPmap[0]->vPmap.size() == Usize[0])) // once we have everybody local +if ((cIdx == 0) + && (static_cast(pPmap[0]->vPmap.size()) == Usize[0])) // once we have everybody local { // we can decide who the leader will be for any intercomms. if (pPmap[0]->U.Mothership.size()) Lrank = *min_element(pPmap[0]->U.Mothership.begin(),pPmap[0]->U.Mothership.end()); @@ -551,7 +569,7 @@ if (cIdx < 0) { // No LogServer. Best we can do is hope we are the Root process and can output a message. // Later this might become more sophisticated and attempt to send a message to Root. - if (Urank == pPmap[0]->U.Root) printf("Error: attempted to Post a message %d without a LogServer\n"); + if (Urank == pPmap[0]->U.Root) printf("Error: attempted to Post a message without a LogServer\n"); return false; } PMsg_p Pkt; diff --git a/Source/Common/CommonBase.h b/Source/Common/CommonBase.h index 84cd069a..b51cf4ef 100644 --- a/Source/Common/CommonBase.h +++ b/Source/Common/CommonBase.h @@ -5,6 +5,7 @@ #include #include using namespace std; +#include "Debug.h" #include "PMsg_p.hpp" #include "ProcMap.h" #include "pthread.h" @@ -51,8 +52,6 @@ void SendPMap(MPI_Comm,PMsg_p*); int RootCIdx(); void MPISpinner(); -const int MPICli = 0; - public: vector FnMapx; vector Comms; // this could be a map indexed by service name @@ -70,7 +69,7 @@ string Ssource; string Sbinary; string STIME; string SDATE; -vector pPmap; // if Comms is a map, this must be too +vector pPmap; int MPI_provided; pthread_t MPI_accept; @@ -83,8 +82,7 @@ MPI_Comm Tcomm; // New comm set up by an MPI_Comm_accept private: char * MPI_Buf; int Msgbufsz; -const int LOG_MSGBUF_BLK_SZ = 14; -const int MSGBUF_BLK_MASK = (1<mailboxWordLength; returnValue += boardComponent << offset; - offset += format->boardWordLength; - returnValue += boxComponent << offset; + if (!IGNORE_BOX_ADDRESS_COMPONENT) + { + offset += format->boardWordLength; + returnValue += boxComponent << offset; + } return returnValue; } @@ -98,11 +101,15 @@ void HardwareAddress::populate_from_software_address(P_addr* source) void HardwareAddress::set_box(AddressComponent value) { /* Validate */ - if (value >= std::pow(2, format->boxWordLength)) + if (!IGNORE_BOX_ADDRESS_COMPONENT) { - throw InvalidAddressException( - dformat("[ERROR] Box component value \"%d\" does not fit format " - "with box length \"%d\".", value, format->boxWordLength)); + if (value >= std::pow(2, format->boxWordLength)) + { + throw InvalidAddressException( + dformat("[ERROR] Box component value \"%d\" does not fit " + "format with box length \"%d\".", + value, format->boxWordLength)); + } } /* Set */ @@ -195,7 +202,8 @@ void HardwareAddress::Dump(FILE* file) /* Just contains the components, and whether or not they have been * defined. */ fprintf(file, "boxComponent: %u ", boxComponent); - fprintf(file, "%s\n", is_box_defined() ? "" : "(not defined)"); + fprintf(file, "%s", is_box_defined() ? "" : "(not defined)"); + fprintf(file, "%s", IGNORE_BOX_ADDRESS_COMPONENT ? " (ignored)\n" : "\n"); fprintf(file, "boardComponent: %u ", boardComponent); fprintf(file, "%s\n", is_board_defined() ? "" : "(not defined)"); diff --git a/Source/Common/HardwareModel/HardwareAddress.h b/Source/Common/HardwareModel/HardwareAddress.h index 3a3fe2a2..9065f8db 100644 --- a/Source/Common/HardwareModel/HardwareAddress.h +++ b/Source/Common/HardwareModel/HardwareAddress.h @@ -28,6 +28,14 @@ #include "P_addr.h" #include /* For validating address components. */ +/* The current Tinsel design does not use a box component for addresses. This + * class is designed to operate in both the cases where the box component + * matters, and where it does not, at compile time. + * + * If the following directive maps true, we still populate the box component, + * but we don't validate it, use it, or check it in any way. */ +#define IGNORE_BOX_ADDRESS_COMPONENT true + /* Define masks for setting the 'definitions' variable. */ #define HARDWARE_ADDRESS_FULLY_DEFINED_MASK 31 /* 0b11111 */ #define BOX_DEFINED_MASK 1 /* 0b00001 */ @@ -76,9 +84,12 @@ class HardwareAddress: public DumpChan void populate_from_software_address(P_addr* source); void Dump(FILE* = stdout); - /* Defines whether or not the hardware address is fully defined. */ - inline bool is_fully_defined() - {return definitions == HARDWARE_ADDRESS_FULLY_DEFINED_MASK;} + /* Defines whether or not the hardware address is fully defined, ignoring + * the box component if appropriate. */ + bool is_fully_defined() + {return definitions >= (IGNORE_BOX_ADDRESS_COMPONENT ? + HARDWARE_ADDRESS_FULLY_DEFINED_MASK - BOX_DEFINED_MASK : + HARDWARE_ADDRESS_FULLY_DEFINED_MASK);} inline bool is_box_defined() {return (definitions & BOX_DEFINED_MASK) > 0;} inline bool is_board_defined() diff --git a/Source/Common/HardwareModel/HardwareIterator.cpp b/Source/Common/HardwareModel/HardwareIterator.cpp index efa77db8..54d06e45 100644 --- a/Source/Common/HardwareModel/HardwareIterator.cpp +++ b/Source/Common/HardwareModel/HardwareIterator.cpp @@ -285,7 +285,11 @@ void HardwareIterator::Dump(FILE* file) /* Print whether or not each iterator has changed since the last time it * was polled. */ std::vector itemTypes; - itemTypes.insert(itemTypes.end(), {"board", "mailbox", "core", "thread"}); + itemTypes.push_back("board"); + itemTypes.push_back("mailbox"); + itemTypes.push_back("core"); + itemTypes.push_back("thread"); + for (long unsigned typeIndex = 0; typeIndex < itemTypes.size(); typeIndex++) { diff --git a/Source/Common/HardwareModel/P_core.cpp b/Source/Common/HardwareModel/P_core.cpp index 060d972e..e0eee57f 100644 --- a/Source/Common/HardwareModel/P_core.cpp +++ b/Source/Common/HardwareModel/P_core.cpp @@ -33,8 +33,8 @@ void P_core::clear() /* Clears the dynamically-allocated binaries. */ void P_core::clear_binaries() { - if (dataBinary != 0) delete dataBinary; - if (instructionBinary != 0) delete instructionBinary; + if (dataBinary != PNULL) delete dataBinary; + if (instructionBinary != PNULL) delete instructionBinary; } /* Donates an uncontained thread to this core. Arguments: @@ -94,7 +94,7 @@ void P_core::Dump(FILE* file) NameBase::Dump(file); /* Dump information about binaries. */ - if (dataBinary != 0) + if (dataBinary != PNULL) { fprintf(file, "No data binary assigned to this core.\n"); } @@ -104,7 +104,7 @@ void P_core::Dump(FILE* file) dataBinary->Dump(file); } - if (instructionBinary != 0) + if (instructionBinary != PNULL) { fprintf(file, "No instruction binary assigned to this core.\n"); } diff --git a/Source/Common/HardwareModel/P_core.h b/Source/Common/HardwareModel/P_core.h index abadb355..86cc3075 100644 --- a/Source/Common/HardwareModel/P_core.h +++ b/Source/Common/HardwareModel/P_core.h @@ -16,6 +16,7 @@ #include "Bin.h" #include "DumpUtils.h" #include "NameBase.h" +#include "OSFixes.hpp" #include "OwnershipException.h" #include "P_mailbox.h" #include "P_thread.h" diff --git a/Source/Common/HardwareModel/P_engine.cpp b/Source/Common/HardwareModel/P_engine.cpp index 964306a3..59948e3f 100644 --- a/Source/Common/HardwareModel/P_engine.cpp +++ b/Source/Common/HardwareModel/P_engine.cpp @@ -232,6 +232,51 @@ void P_engine::connect(AddressComponent start, AddressComponent end, } } +/* Grabs all boxes associated with a given task in this engine. Arguments: + * + * - task: Task to search for. + * - boxes: Set to populate with box addresses found. */ +void P_engine::get_boxes_for_task(P_task* task, std::set* boxes) +{ + boxes->clear(); + + HardwareIterator iterator = HardwareIterator(this); + P_box* currentBox; + P_thread* currentThread; + + /* Go throughout the entire hardware stack. */ + while (!iterator.has_wrapped()) + { + currentBox = iterator.get_board()->parent; + currentThread = iterator.get_core()->P_threadm.begin()->second; + + /* If the current board is contained by a box in 'boxes', we skip + * it. */ + if (std::find(boxes->begin(), boxes->end(), currentBox) != + boxes->end()) + { + iterator.next_board(); + } + + /* Otherwise, look at the first thread of this core, and deduce + containment from that. */ + else if (currentThread->P_devicel.size() && + currentThread->P_devicel.front()->par->par == task) + { + /* We got a live one, boss! */ + boxes->insert(currentBox); + iterator.next_board(); + } + + else + { + /* No thread mapping to this task here... lets move on to the next + * core. */ + iterator.next_core(); + } + } +} + /* Write debug and diagnostic information about the POETS engine, recursively, * using dumpchan. Arguments: * @@ -256,7 +301,8 @@ void P_engine::Dump(FILE* file) } if (!version.empty()) { - fprintf(file, "Written for parser version: %s\n", version.c_str()); + fprintf(file, "Written for file reader version: %s\n", + version.c_str()); } if (!fileOrigin.empty()) { diff --git a/Source/Common/HardwareModel/P_engine.h b/Source/Common/HardwareModel/P_engine.h index 135ba8f9..6c0ba4b1 100644 --- a/Source/Common/HardwareModel/P_engine.h +++ b/Source/Common/HardwareModel/P_engine.h @@ -10,10 +10,12 @@ * See the hardware model documentation for further information POETS * engines. */ +#include #include #include "DumpUtils.h" #include "HardwareAddress.h" +#include "HardwareIterator.h" #include "NameBase.h" #include "OrchBase.h" #include "OwnershipException.h" @@ -58,6 +60,10 @@ class P_engine: public NameBase, public DumpChan void connect(AddressComponent start, AddressComponent end, float weight, bool oneWay=false); + /* Convenient way to get all boxes mapped with a given task in the + * engine. */ + void get_boxes_for_task(P_task* task, std::set* boxes); + /* Engines may be created from a configuration file. If so, some portion of * their metadata will be set. */ std::string author; diff --git a/Source/Common/OSFixes.hpp b/Source/Common/OSFixes.hpp index 42d92dbd..1623b4dd 100644 --- a/Source/Common/OSFixes.hpp +++ b/Source/Common/OSFixes.hpp @@ -7,12 +7,12 @@ * ============================================================================= * OS/compiler-specific oddities to make code as portable as possible. * - * + * * ============================================================================= * ===========================================================================*/ /* ============================================================================= - * Avoid format warnings on MINGW from it trying to use old MSVc printf + * Avoid format warnings on MINGW from it trying to use old MSVc printf * ===========================================================================*/ #if defined(__MINGW64__) || defined(__MINGW32__) #define __USE_MINGW_ANSI_STDIO 1 @@ -20,28 +20,30 @@ /* ============================================================================= - * Headers for fixed-width ints (i.e. uint64_t) depend on c++ version. + * Headers for fixed-width ints (i.e. uint64_t) depend on c++ version. * These are needed for addresses. * ===========================================================================*/ -#if __cplusplus >= 201103 +#if __cplusplus >= 201103 #include #include #else #include - #include + #ifndef __BORLANDC__ // Borland doesn't know about inttypes.h, + #include // despite knowing about stdint.h... + #endif #include #endif /* ============================================================================= - * Pointer/Address formatting depends on C++ & Compiler version. + * Pointer/Address formatting depends on C++ & Compiler version. * * C++98 doesn't necessarily know about long-long and long is not 64-bits on all * compiler/platform combinations. To complicate matters, uint64_t (inittpypes.h - * /cinittypes) is not consistently typed across platforms. e.g. it is long on - * Ubuntu 16.04 GCC but long long on MinGW 64-bit. + * /cinittypes) is not consistently typed across platforms. e.g. it is long on + * Ubuntu 16.04 GCC but long long on MinGW 64-bit. * Relies on inttypes.h/cinittypes. * - * Example Usage: + * Example Usage: * fprintf(fp, "Pointer: %" PTR_FMT "\n", static_cast(&Var); * ===========================================================================*/ #define PTR_FMT "#018" PRIx64 @@ -91,7 +93,7 @@ * When called with "errno", this will return an std::string containing the * relevant error text for the last failed system call. * - * The "default" option (using sterror) is NOT thread safe, but is portable. + * The "default" option (using sterror) is NOT thread safe, but is portable. * Other options (using sterror_s and friends) need adding for Windows/Borland. * * Example Usage: @@ -107,7 +109,7 @@ namespace POETS #elif _POSIX_C_SOURCE >= 200112L char errMsg[1000]; if(strerror_r(errNum, errMsg, 1000)) return std::string(""); - else return std::string(errMsg); + else return std::string(errMsg); #else return std::string(strerror(errNum)); #endif @@ -121,34 +123,129 @@ namespace POETS * code is inserted into pre-defined methods. * * OS_ATTRIBUTE_UNUSED Placed immedidiately after potentially unused var. - * OS_PRAGMA_UNUSED(x) Placed on a line after a potentially unused var. + * OS_PRAGMA_UNUSED(x) Placed on a line after a potentially unused var. * * Example Usage: * unsigned foo OS_ATTRIBUTE_UNUSED = 0; - * OS_PRAGMA_UNUSED(foo) // N.B. note the lack of ";" + * OS_PRAGMA_UNUSED(foo) // N.B. note the lack of ";" * ===========================================================================*/ #ifdef __GNUC__ /* GCC Specific definitions. */ -#define OS_ATTRIBUTE_UNUSED __attribute__((unused)) -#define OS_PRAGMA_UNUSED(x) +#define OS_ATTRIBUTE_UNUSED __attribute__((unused)) +#define OS_PRAGMA_UNUSED(x) #elif __BORLANDC__ /* Borland specific definitions. */ -#define OS_ATTRIBUTE_UNUSED +#define OS_ATTRIBUTE_UNUSED #define OS_PRAGMA_UNUSED(x) (void) x ; #elif __clang__ /* Clang Specific definitions. */ -#define OS_ATTRIBUTE_UNUSED +#define OS_ATTRIBUTE_UNUSED #define OS_PRAGMA_UNUSED(x) #pragma unused(x); #elif _MSC_VER /* MS Specific definitions. */ -#define OS_ATTRIBUTE_UNUSED +#define OS_ATTRIBUTE_UNUSED #define OS_PRAGMA_UNUSED(x) x; #else /* Other compilers. */ #warning "Building without Compiler-specific unused variable handling." -#define OS_ATTRIBUTE_UNUSED -#define OS_PRAGMA_UNUSED(x) +#define OS_ATTRIBUTE_UNUSED +#define OS_PRAGMA_UNUSED(x) +#endif + +/* ============================================================================= + * Hostname retrieval, as a string. + * ===========================================================================*/ +#ifdef _WIN32 +#else /* Unix-like way */ +#include +#define HOSTNAME_LENGTH 128 +#endif + +namespace POETS +{ + inline std::string get_hostname() + { + #ifdef _WIN32 + printf("[POETS::get_hostname] MLV should edit this to use " + "GetComputerNameExA!\n"); // + return ""; + #else + char buffer[HOSTNAME_LENGTH]; + gethostname(buffer, HOSTNAME_LENGTH); + return buffer; + #endif + } +} + +/* ============================================================================= + * Filesystem manipulation - not under RISCV though. + * ===========================================================================*/ + +#ifndef __riscv +#ifdef _WIN32 +#include +#include +#define MAXIMUM_PATH_LENGTH MAX_PATH /* Actually, MAX_PATH isn't. See +https://stackoverflow.com/questions/833291/is-there-an-equivalent-to-winapis +-max-path-under-linux-unix */ +#else /* Unix-like way */ +#include +#include +#include +#define MAXIMUM_PATH_LENGTH PATH_MAX /* Actually, PATH_MAX isn't. See +http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html */ #endif +namespace POETS +{ + /* Returns the directory component of 'fullPath'. Don't pass in a path to a + * directory unless you know what you're doing. */ + inline std::string dirname(std::string fullPath) + { + #ifdef _WIN32 + printf("[POETS::dirname] MLV should edit this to use " + "Generics/filename!\n"); // + return ""; + #else + /* FYI: The function '::dirname' refers to dirname in the global + * namespace. Another pitfall: The const_cast is implicitly trusting + * ::dirname not to modify the path passed in. */ + return ::dirname(const_cast(fullPath.c_str())); + #endif + } + + /* Gets the path to the executable calling this function. Note that this + * will not get the object. Returns a string containing the path if + * successful, and returns an empty string otherwise. */ + inline std::string get_executable_path() + { + char path[MAXIMUM_PATH_LENGTH] = {}; /* Empty initialiser to placate + * Valgrind. */ + + /* Determining the predicate by OS. Value resolution is independent. */ + #ifdef _WIN32 + HMODULE hModule = GetModuleHandle(PNULL); + if (hModule != PNULL) + { + GetModuleFileName(hModule, path, sizeof(path)); + + #else /* Unix-like way. If you want to harden this further, look at the + * man page for readlink, at: + * http://man7.org/linux/man-pages/man2/readlink.2.html#EXAMPLE */ + if (readlink("/proc/self/exe", path, MAXIMUM_PATH_LENGTH)) + { + + #endif + return path; + } + + else + { + return ""; + } + } +} +#endif /* _riscv */ + /* ===========================================================================*/ #endif /* __OSFIXES__H */ diff --git a/Source/Common/Pglobals.h b/Source/Common/Pglobals.h index dde7c3db..d62fc70f 100644 --- a/Source/Common/Pglobals.h +++ b/Source/Common/Pglobals.h @@ -199,7 +199,8 @@ static const byte ROOT = 0x00; #define csINJECTORproc "Injector:CommonBase" #define csNAMESERVERproc "NameServer:SBase:CommonBase" #define csMONITORproc "Monitor:CommonBase" -#define csMOTHERSHIPproc "TMoth:CommonBase" +#define csMOTHERSHIPproc "Mothership:SBase:CommonBase" +//#define csMOTHERSHIPproc "TMoth:CommonBase" #define csMPITESTproc "MPITest:CommonBase" // tag defined as a directive because MPI libraries are c-based, have no concept diff --git a/Source/Common/ProcMap.cpp b/Source/Common/ProcMap.cpp index fa21313a..34b11111 100644 --- a/Source/Common/ProcMap.cpp +++ b/Source/Common/ProcMap.cpp @@ -4,6 +4,8 @@ #include "CommonBase.h" #include "Pglobals.h" +#include "OSFixes.hpp" + //============================================================================== ProcMap::ProcMap(CommonBase * p) @@ -26,7 +28,9 @@ void ProcMap::Dump(FILE * fp) fprintf(fp,"Process Map start\n" "++++++++++++++++++++++++++++++++++++++" "++++++++++++++++++++++++++++++++++++++\n"); -fprintf(fp,"Me,Parent 0x%#018x,0x%#016x\n",(void *)this,(void *)par); +//fprintf(fp,"Me,Parent 0x%#018x,0x%#016x\n",(void *)this,(void *)par); +fprintf(fp,"Me,Parent %" PTR_FMT ",",reinterpret_cast(this)); +fprintf(fp,"%" PTR_FMT "\n",reinterpret_cast(par)); fprintf(fp,"Process map (%u entries):\n",static_cast(vPmap.size())); WALKVECTOR(ProcMap_t,vPmap,i) { fprintf(fp,"\n- - - - - - - - - - - - - - - - - - - " diff --git a/Source/Dummy/Dummy.cpp b/Source/Dummy/Dummy.cpp index b327756a..a796d778 100644 --- a/Source/Dummy/Dummy.cpp +++ b/Source/Dummy/Dummy.cpp @@ -7,6 +7,8 @@ #include "Pglobals.h" #include +#include "OSFixes.hpp" + //============================================================================== Dummy::Dummy(int argc,char * argv[],string d): @@ -52,7 +54,20 @@ fprintf(fp,"Dummy dump+++++++++++++++++++++++++++++++++++\n"); printf("Key Method\n"); WALKVECTOR(FnMap_t*,FnMapx,F) { -WALKMAP(unsigned,pMeth,(**F),i)printf("%#010x %#016x\n",(*i).first,(*i).second); +WALKMAP(unsigned,pMeth,(**F),i) +{ + //fprintf(fp,"%#010x 0x%#016x\n",(*i).first,(*i).second); + fprintf(fp,"%#010x ",(*i).first); + + // Now for a horrible double type cast to get us a sensible function pointer. + // void*s are only meant to point to objects, not functions. So we get to a + // void** as a pointer to a function pointer is an object pointer. We can then + // follow this pointer to get to the void*, which we then reinterpret to get + // the function's address as a uint64_t. + fprintf(fp,"%" PTR_FMT "\n",reinterpret_cast( + *(reinterpret_cast(&((*i).second)))) + ); +} } fprintf(fp,"Dummy dump-----------------------------------\n"); CommonBase::Dump(fp); diff --git a/Source/Injector/InjectorMain.cpp b/Source/Injector/InjectorMain.cpp index 12624415..dad2a475 100644 --- a/Source/Injector/InjectorMain.cpp +++ b/Source/Injector/InjectorMain.cpp @@ -13,11 +13,11 @@ Injector * pInjector = 0; try { pInjector = new Injector(argc,argv,string(csINJECTORproc)); } -catch(bad_alloc) { +catch(bad_alloc&) { printf("\n\n%s Main out of memory... \n\n",csINJECTORproc); fflush(stdout); } -catch(Unrec_t u) { +catch(Unrec_t& u) { u.Post(); } catch(...) { diff --git a/Source/Launcher/Call.cpp b/Source/Launcher/Call.cpp new file mode 100644 index 00000000..9ae1488c --- /dev/null +++ b/Source/Launcher/Call.cpp @@ -0,0 +1,118 @@ +#include "Call.h" + +/* Runs a command, returning the exit code. Blocks until the command is + * complete. + * + * This method uses the fork-pipe pattern for executing arbitrary commands. In + * brief, this process is forked into a caller and a reader process; the caller + * process hooks up stdout and stderr pipes, and runs a command using execvp + * (then closes), whereas the reader listens on the other side of the pipe to + * gather stdout and stderr. + * + * Arguments: + * + * - command: command to run, passed naively into the shell. + * - stdout: string populated with the contents of the standard output. + * - stderr: string populated with the contents of the standard error, or other + * connection errors. + * + * Returns the exit code of the called process. */ +int Call::call(std::vector command, std::string* stdout, + std::string* stderr) +{ + stderr->clear(); + stdout->clear(); + + /* Construct some pipes for reading stdout and stderr. */ + int stdoutPipe[2]; + int stderrPipe[2]; + pipe(stdoutPipe); + pipe(stderrPipe); + + /* They should not block. */ + fcntl(stdoutPipe[0], F_SETFL, O_NONBLOCK); + fcntl(stderrPipe[0], F_SETFL, O_NONBLOCK); + + /* Grab the executor PID for the examiner, so we can get the return + * code. */ + pid_t pid = fork(); + + /* Process executor */ + if (!pid) + { + /* Map pipes to stdout and stderr. */ + dup2(stdoutPipe[1], STDOUT_FILENO); + dup2(stderrPipe[1], STDERR_FILENO); + + /* Close all descriptors - we won't need them where we're going. */ + close(stdoutPipe[0]); + close(stdoutPipe[1]); + close(stderrPipe[0]); + close(stderrPipe[1]); + + /* Run the command, supplanting our existing execution. execvp has + * the somewhat pathological signature of + * + * execvp(const char* file, char *const argv), + * + * so we need to convert our command into a char *const, with a NULL at + * the end. */ + + /* argv must be dynamic, because its size is a function of command. We + * also need to include space for a NULL terminator. + * + * Note that we don't delete argv - this is okay, because execvp + * replaces the entire process anyway. */ + const char** argv = new const char*[command.size() + 1]; + + /* Populate argv from command. */ + for (unsigned it=0; it > outChannels; /* pipe and str */ + std::vector >::iterator outChannel; + outChannels.push_back(std::make_pair(stdoutPipe[0], stdout)); + outChannels.push_back(std::make_pair(stderrPipe[0], stderr)); + + for (outChannel=outChannels.begin(); + outChannel!=outChannels.end(); outChannel++) + { + do { + /* Read in chunks. */ + const ssize_t amountRead = read(outChannel->first, buffer, + STDOUTERR_BUFFER_SIZE); + if (amountRead > 0) + { + outChannel->second->append(buffer, amountRead); + } + else break; /* read returns -1 if nothing was there to read, and if + * the pipe is still open. */ + + } while (errno == EAGAIN || errno == EINTR); + } + + /* Close up shop. */ + close(stdoutPipe[0]); + close(stdoutPipe[1]); + close(stderrPipe[0]); + close(stderrPipe[1]); + return exitCode; +} diff --git a/Source/Launcher/Call.h b/Source/Launcher/Call.h new file mode 100644 index 00000000..128947d8 --- /dev/null +++ b/Source/Launcher/Call.h @@ -0,0 +1,27 @@ +#ifndef __ORCHESTRATOR_SOURCE_LAUNCHER_COMMAND_H +#define __ORCHESTRATOR_SOURCE_LAUNCHER_COMMAND_H + +/* Defines a method that allows you to perform system commands and retrieve + * stdout/stderr/rc. Will certainly not work on Windows. */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "OSFixes.hpp" + +#define STDOUTERR_BUFFER_SIZE 4096 + +namespace Call +{ + int call(std::vector command, std::string* stdout, + std::string* stderr); +} + +#endif diff --git a/Source/Launcher/LauncherMain.cpp b/Source/Launcher/LauncherMain.cpp new file mode 100644 index 00000000..c72a1a6f --- /dev/null +++ b/Source/Launcher/LauncherMain.cpp @@ -0,0 +1,814 @@ +/* This file contains the `main` free function for the Orchestrator-MPI + * launcher. + * + * If you're interested in input arguments, call with "/f", or read the + * helpDoc variable in Launcher::ParseArgs. */ + +#include "LauncherMain.h" + +/* Calls 'Launch' in a try/catch block. */ +int main(int argc, char** argv) +{ + /* Run the launcher in a try/catch block. */ + try + { + return Launcher::Launch(argc, argv); + } + + catch(std::bad_alloc& e) + { + printf("%sWe caught a bad_alloc: %s. Closing.\n", + Launcher::errorHeader, e.what()); + fflush(stdout); + return 1; + } + + catch(...) + { + printf("%sUnhandled exception caught in main. Closing.\n", + Launcher::errorHeader); + fflush(stdout); + return 1; + } +} + +namespace Launcher /* Who indents function definitions in namespaces, */ +{ /* honestly? */ + +/* I mean, you could break this if you really wanted to, but that would make + * you an arse. */ +bool AreWeRunningOnAPoetsBox() +{ + return file_exists(fileOnlyOnPoetsBox); +} + +/* Constructs the MPI command to run and writes it to 'command', given: + * + * - useMotherships: Whether or not to create any motherships at all. + * - internalPath: Path to define LD_LIBRARY_PATH on child processes. + * - overrideHost: Host to override, if applicable. + * - batchPath: Path to a batch script to run on root when everything kicks + * off. + * - hdfPath: Path to a topology file to load on root when everything kicks + * off. + * - mothershipHosts: If not overriding, spawn motherships on these hosts. If + * this is empty and useMotherships is true, we start a mothership on this + * machine. + * - executablePaths: For remote hosts, this should contain the directory + * containing the executable to run (-wdir). + * - gdbProcs: Process names to run GDB on. + * - valgrindProcs: Process names to run Valgrind on. + * - command: Output, where the command is written to. It's up to the caller to + examine and run it as they desire. */ +void BuildCommand(bool useMotherships, std::string internalPath, + std::string overrideHost, std::string batchPath, + std::string hdfPath, + std::set mothershipHosts, + std::map executablePaths, + std::map gdbProcs, + std::map valgrindProcs, + std::string* command) +{ + std::stringstream commandStream; + + /* Will be populated with processes to spawn. Members are on the heap + * (bloody stringstreams). */ + std::vector hydraProcesses; + + /* Will be populated with hosts, to define which processes run on which + * hosts in the command. Members are *not* on the heap. */ + std::vector orderedHosts; + + /* We'll need our hostname. */ + std::string ourHostname = POETS::get_hostname(); + + /* Boilerplate */ + commandStream << "mpiexec.hydra"; + if (!internalPath.empty()) + { + commandStream << " -genv LD_LIBRARY_PATH \"" + << internalPath + << "\""; + } + + /* Prepend rank to stdout in debug mode. */ + if (ORCHESTRATOR_DEBUG) commandStream << " -l"; + + /* Standard-issue processes (sorry). */ + std::string localBinDir = POETS::dirname(POETS::get_executable_path()); + hydraProcesses.push_back(new std::stringstream); + + /* Root */ + orderedHosts.push_back(ourHostname); + *(hydraProcesses.back()) << "-n 1 "; + if (gdbProcs[execRoot]) *(hydraProcesses.back()) << execGdb << " "; + if (valgrindProcs[execRoot]) *(hydraProcesses.back()) << execValgrind + << " "; + *(hydraProcesses.back()) << localBinDir << "/" << execRoot; + + /* Root args */ + if (!hdfPath.empty()) /* Pass HDF, quoted, to root. */ + { + *(hydraProcesses.back()) << " /" << ROOT_ARG_HDF + << "=\\\"" << hdfPath << "\\\""; + } + + if (!batchPath.empty()) /* Pass batch script, quoted, to root. */ + { + *(hydraProcesses.back()) << " /" << ROOT_ARG_BATCH + << "=\\\"" << batchPath << "\\\""; + } + + /* Logserver */ + hydraProcesses.push_back(new std::stringstream); + orderedHosts.push_back(ourHostname); + *(hydraProcesses.back()) << "-n 1 "; + if (gdbProcs[execLogserver]) *(hydraProcesses.back()) << execGdb << " "; + if (valgrindProcs[execLogserver]) *(hydraProcesses.back()) << execValgrind + << " "; + *(hydraProcesses.back()) << localBinDir << "/" << execLogserver; + + /* Clock */ + hydraProcesses.push_back(new std::stringstream); + orderedHosts.push_back(ourHostname); + if (gdbProcs[execClock]) *(hydraProcesses.back()) << execGdb << " "; + if (valgrindProcs[execClock]) *(hydraProcesses.back()) << execValgrind + << " "; + *(hydraProcesses.back()) << "-n 1 "; + *(hydraProcesses.back()) << localBinDir << "/" << execClock; + + /* Adding motherships... */ + if (useMotherships) + { + /* If we've set the override, just spawn a mothership on that host. */ + if (!overrideHost.empty()) + { + hydraProcesses.push_back(new std::stringstream); + orderedHosts.push_back(overrideHost); + *(hydraProcesses.back()) + << "-n 1 -wdir ~/" << deployDir << " ./" << execMothership; + } + + /* Otherwise, if there are no hosts, spawn a mothership on this box + * (we've already checked that it's a POETS box). */ + else if (mothershipHosts.empty()) + { + hydraProcesses.push_back(new std::stringstream); + orderedHosts.push_back(ourHostname); + + *(hydraProcesses.back()) << "-n 1 " << localBinDir << "/" + << execMothership; + } + + /* Otherwise, spawn one mothership for each host. */ + else + { + WALKSET(std::string, mothershipHosts, host) + { + hydraProcesses.push_back(new std::stringstream); + orderedHosts.push_back(*host); + *(hydraProcesses.back()) + << "-n 1 -wdir ~/" << executablePaths[(*host)] + << " ./" << execMothership; + } + } + } + + /* Construct the host list, and append it to the command. Example: + * + * If orderedHosts is [hostA, hostA, hostB, hostC], then this adds + * " -hostlist hostA:2,hostB:1,hostC:1". + * + * If orderedHosts is [hostD, hostE, hostE, hostD], then this adds + * " -hostlist hostD:1,hostE:2,hostD:1" (so we can't simply count the + * occurences...) */ + std::stringstream hostlistStream; + std::string currentHost; + unsigned hostStreak = 0; + unsigned index; + hostlistStream << " -hostlist "; /* There's always going to be at least + * one. */ + for (index=0; indexstr(); + for (index=1; indexstr()); + *command = commandStream.str(); + + /* Cleanup. */ + for (index=0; index* hosts, + std::map* paths) +{ + paths->clear(); + + /* Save us from ourselves. */ + if (hosts->empty()) return 0; + + /* Figure out where the executables all are on this box. We assume that + * they are in the same directory as the launcher. */ + DebugPrint("%sIdentifying where the binaries are on this box, from where " + "the launcher is...\n", debugHeader); + std::string sourceDir; + sourceDir = POETS::dirname(POETS::get_executable_path()); + + if (sourceDir.empty()) + { + printf("%sCould not identify where the binaries are on this " + "box. Closing.\n", errorHeader); + return 1; + } + else + { + DebugPrint("%sFound the binaries to copy at '%s'.\n", debugHeader, + sourceDir.c_str()); + } + + /* Deploy! */ + std::string stdout; + std::string stderr; + WALKSET(string, (*hosts), host) + { + DebugPrint("%sDeploying to host '%s'...\n", + debugHeader, host->c_str()); + + /* Ensure .orchestrator exists. */ + if (SSH::call((*host), + dformat("mkdir --parents \"%s\"\n", + POETS::dirname(deployDir).c_str()), + &stdout, &stderr) > 0) + { + printf("%sSSH command to host '%s' failed (can you connect to the " + "host by SSH?): %s", + errorHeader, (*host).c_str(), stderr.c_str()); + return 1; + } + + /* Remove the target directory, dangerously. */ + if (SSH::call((*host), + dformat("rm --force --recursive \"%s\"\n", deployDir), + &stdout, &stderr) > 0) + { + /* NB: rm -rf can't fail outside mad edge cases... */ + printf("%sSSH command to host '%s' failed (can you connect to the " + "host by SSH?): %s", + errorHeader, (*host).c_str(), stderr.c_str()); + return 1; + } + + /* Deploy binaries. */ + if (SSH::deploy_directory((*host), sourceDir, deployDir, + &stdout, &stderr) > 0) + { + printf("%sFailed to deploy to host '%s': %s", + errorHeader, (*host).c_str(), stderr.c_str()); + return 1; + } + + /* Grab the full path of the directory created (we can't compute that + * here, because user names may vary, etc.) */ + if (SSH::call((*host), + dformat("realpath \"%s\" | tr --delete '\n'\n", + deployDir), + &stdout, &stderr) > 0) + { + printf("%sSSH command to host '%s' failed (can you connect to the " + "host by SSH?): %s", + errorHeader, (*host).c_str(), stderr.c_str()); + return 1; + } + + DebugPrint("%sDeployment to host '%s' complete.\n", + debugHeader, host->c_str()); + + (*paths)[*host] = stdout; + } + + return 0; +} + +/* Reads the hardware description file at hdfPath, and populates the vector at + * hosts with the names of hosts obtained from that file. Returns 0 if all is + * well, and 1 if we need to leave. */ +int GetHosts(std::string hdfPath, std::set* hosts) +{ + if (!file_readable(hdfPath.c_str())) + { + printf("%sCannot find hardware description file at %s. Closing.\n", + errorHeader, hdfPath.c_str()); + return 1; + } + +// Take it away, ADB. +JNJ Jh(hdfPath); // OK, let's do it +vH sects; +Jh.FndSect("engine_box",sects); // Got all the sections called .... +if (sects.empty()) return 0; // None there? +WALKVECTOR(hJNJ,sects,i) { // Walk all the sections... + vH recds; + Jh.GetRecd(*i,recds); // Get the records + WALKVECTOR(hJNJ,recds,j) { // Walk the records + vH varis; + Jh.GetVari(*j,varis); // Get the variables (box names) + WALKVECTOR(hJNJ,varis,k) { + if ((*k)->str.empty()) continue; // If it's not blank..... + if ((*k)->qop==Lex::Sy_plus) continue; // If it's not a '+'..... + vH subs; + Jh.GetSub(*k,subs); // Get the box variable subname(s) + WALKVECTOR(hJNJ,subs,l) { // Walk them + if ((*l)->str=="hostname") { // Look for..... + vH subs2; + Jh.GetSub(*l,subs2); // And get them + if (subs2.empty()) continue; // Non-empty set? + hosts->insert(subs2[0]->str); // At last! Save the damn thing + } + } + } + } +} +return 0; +} + +/* Launches the Orchestrator, unsurprisingly. */ +int Launch(int argc, char** argv) +{ + /* Parse input arguments. */ + std::string batchPath; + std::string hdfPath; + bool useMotherships; + bool dryRun; + std::string overrideHost; + std::string internalPath; + std::map gdbProcs; + std::map valgrindProcs; + gdbProcs["root"] = false; + gdbProcs["rtcl"] = false; + gdbProcs["logserver"] = false; + gdbProcs["mothership"] = false; + valgrindProcs["root"] = false; + valgrindProcs["rtcl"] = false; + valgrindProcs["logserver"] = false; + valgrindProcs["mothership"] = false; + + if (ParseArgs(argc, argv, &batchPath, &hdfPath, &useMotherships, &dryRun, + &overrideHost, &internalPath, &gdbProcs, + &valgrindProcs) > 0) return 1; + + /* If the default hardware description file path has a file there, and we + * weren't passed a file explicitly, let's roll with the one we've + * found. + * + * If the operator doesn't like this default, they can always load a new + * one in the usual way, which will clobber the target. file_exists is from + * flat.h. */ + if (hdfPath.empty() && file_exists(defaultHdfPath)) + { + hdfPath = defaultHdfPath; + DebugPrint("%sFound a hardware description file in the default " + "search location (%s). Using that one.\n", + debugHeader, hdfPath.c_str()); + } + + /* Read the input file, if supplied, and get a set of hosts we must launch + * Mothership processes on. Don't bother if we're overriding, or if + * motherships are disabled. + * + * Future work, we may want to make hosts a std::map, so + * that you can launch specific processes with it from a more advanced + * input file. Just a passing thought. */ + std::set hosts; /* May remain empty. */ + if (useMotherships) /* User may have disabled them, in which case there's + * no point in us looking. */ + { + /* Have we been given a file to read, and it hasn't been overridden? */ + if (!hdfPath.empty() and overrideHost.empty()) + { + int rc = GetHosts(hdfPath, &hosts); + if (rc != 0) return rc; + + /* Print the hosts we found, if anyone is listening. */ + #if ORCHESTRATOR_DEBUG + if(!hosts.empty()) + { + DebugPrint("%sAfter reading the input file, I found the " + "following hosts:\n", debugHeader); + WALKSET(std::string, hosts, hostIterator) + { + DebugPrint("%s%s- %s\n", debugHeader, debugIndent, + (*hostIterator).c_str()); + } + DebugPrint("%s\n", debugHeader); + } + + else + { + DebugPrint("%sAfter parsing the input file, I found no " + "hosts.\n", debugHeader); + } + #endif + } + + /* If we're overriding, that makes the host list quite simple... */ + else if (!overrideHost.empty()) + { + hosts.insert(overrideHost); + DebugPrint("%sIgnoring input file, and instead using the override " + "passed in as an argument.\n", debugHeader); + } + } + + /* Warn the user that we can't spawn any motherships iff: + * - the user hasn't disabled motherships, + * - no hosts were found from any input file passed in, + * - no override host was proposed, and + * - we're not running on a POETS box. */ + DebugPrint("%sPerforming POETS box check...\n", debugHeader); + if (useMotherships && hosts.empty() && !AreWeRunningOnAPoetsBox()) + { + printf("[WARN] Launcher: Not running on a POETS box, and found no " + "motherships running on alternative machines, so we're not " + "spawning any mothership processes.\n"); + useMotherships = false; + } + + /* Deploy the binaries to the hosts that are running motherships, if we're + * using motherships. If we do deploy anything, store the paths that we've + * deployed stuff to, so that we can build the command later. Fail fast. */ + std::map deployedPaths; + if (DeployBinaries(&hosts, &deployedPaths) != 0) return 1; + + /* Build the MPI command. */ + std::string command; + DebugPrint("%sBuilding command...\n", debugHeader); + BuildCommand(useMotherships, internalPath, overrideHost, batchPath, + hdfPath, hosts, deployedPaths, gdbProcs, valgrindProcs, + &command); + + /* Run the MPI command, or not. */ + DebugPrint("%sRunning this command: %s\n", debugHeader, command.c_str()); + if (dryRun) + { + #if ORCHESTRATOR_DEBUG + #else + printf("%s\n", command.c_str()); + #endif + return 0; + } + + /* Note we don't use call here, because we don't care about capturing the + * stdout, stderr, or returncode (though we might as well propagate the + * latter). */ + return system(command.c_str()); +} + +/* Parses arguments given to the launcher on the command line, and populates + * various reference-passed variables as it goes. May also do some + * printing. Arguments: + * + * - argc: Input argument count + * - argv: Input argument vector (har har) + * - batchPath: Output, populated with the path to a batch script from + * arguments (or empty if not defined). + * - hdfPath: Output, populated with the path to a hardware description file + * to read from arguments (or empty if not defined). + * - useMotherships: Output, whether or not motherships have been explicitly + * disabled. + * - dryRun: Output, true if the caller wants to just see what would be + * run. Still deploys binaries. + * - overrideHost: Output, populated with the name of the host to run a + * mothership on, regardless of whether or not an input file has been passed + * in (or empty if not defined). + * - internalPath: Output, holds the internal path argument if defined, see the + * help string. + * - gdbProcs: Processes to point GDB at. + * - valgrindProcs: Processes to point Valgrind at. + * + * Returns non-zero if a fast exit is needed. */ +int ParseArgs(int argc, char** argv, std::string* batchPath, + std::string* hdfPath, bool* useMotherships, bool* dryRun, + std::string* overrideHost, std::string* internalPath, + std::map* gdbProcs, + std::map* valgrindProcs) +{ + /* Print input arguments, if we're in debug mode. */ + DebugPrint("%sWelcome to the POETS Launcher. Raw arguments:\n", + debugHeader); + #if ORCHESTRATOR_DEBUG + for (int argIndex=0; argIndex argKeys; + argKeys["batchPath"] = "b"; + argKeys["dontStartTheOrchestrator"] = "d"; + argKeys["hdfPath"] = "f"; + argKeys["gdb"] = "g"; + argKeys["help"] = "h"; + argKeys["noMotherships"] = "n"; + argKeys["override"] = "o"; + argKeys["internalPath"] = "p"; + argKeys["valgrind"] = "v"; + + /* Defines help string, printed when user calls with `-h`. */ + std::string helpDoc = dformat( +"Usage: %s [/b = FILE] [/d] [/f = FILE] [/h] [/g = PROCESS] [/n] [/o = HOST] [/p = PATH] [/v = PROCESS]\n" +"\n" +"This is the Orchestrator launcher. It starts the following Orchestrator processes:\n" +"\n" +" - root\n" +" - logserver\n" +" - rtcl\n" +" - mothership (some number of them)\n" +"\n" +"For verbosity, compile with debugging enabled by setting ORCHESTRATOR_DEBUG to 1 (i.e. `make debug`, or `$(CXX) -DORCHESTRATOR_DEBUG`. See `Debug.{cpp,h}`). This launcher accepts the following optional arguments:\n" +"\n" +"\t/%s = FILE: Path to a batch script to run on the root process on startup. Each command is added to root's MPI message queue, and each runs independently of the other. See the Orchestrator documentation for more information on the batch system.\n" +"\n" +"\t/%s: Don't start the Orchestrator! Still deploys binaries, but does not execute the command. Instead, the command is printed in your shell.\n" +"\n" +"\t/%s = FILE: Path to a hardware file to read hostnames from, in order to get hostnames for starting remote mothership processes. If none is provided, '%s' is searched (this default is defined in the launcher source, and is not yet read from any configuration file). If the operator does not wish to use this default, the operator can reset the topology in the root shell using a 'topology' command.\n" +"\n" +"\t/%s: Prints this help text.\n" +"\n" +"\t/%s: Points gdb (%s) at one of the non-mothership processes listed above. Will not work as you indend on remote processes (unless you're smarter than I am).\n" +"\n" +"\t/%s: Does not spawn any mothership processes, regardless of other arguments you pass in.\n" +"\n" +"\t/%s = HOST: Override all Mothership hosts, specified from a hardware description file, with HOST. Using this option will only spawn one mothership process (unless /%s is used, in which case no mothership processes are spawned).\n" +"\n" +"\t/%s = PATH: Define an LD_LIBRARY_PATH environment variable for all spawned processes. This is useful for defining where shared object files can be found by children.\n" +"\t/%s: Points valgrind (%s) at one of the processes listed above, except mothership. Combine with /%s at your own risk.\n" +"\n" +"If you are still bamboozled, or you're a developer, check out the Orchestrator documentation.\n", +argv[0], +argKeys["batchPath"].c_str(), +argKeys["dontStartTheOrchestrator"].c_str(), +argKeys["hdfPath"].c_str(), defaultHdfPath, +argKeys["help"].c_str(), +argKeys["gdb"].c_str(), execGdb, +argKeys["noMotherships"].c_str(), +argKeys["override"].c_str(), argKeys["noMotherships"].c_str(), +argKeys["internalPath"].c_str(), +argKeys["valgrind"].c_str(), execValgrind, argKeys["gdb"].c_str()); + + /* Parse the input arguments. */ + std::string concatenatedArgs; + for(int i=1; iempty()) + { + *batchPath = (*argIt).GetP(0); + } + else + { + printf("[WARN] Launcher: Ignoring duplicate input batchPath " + "argument.\n"); + } + } + + if (currentArg == argKeys["dontStartTheOrchestrator"]) + { + if (!*dryRun) + { + *dryRun = true; + } + else + { + printf("[WARN] Launcher: Ignoring duplicate 'don't start the " + "Orchestrator!' argument.\n"); + } + + } + + if (currentArg == argKeys["hdfPath"]) /* Hardware file: Store it. */ + { + if (hdfPath->empty()) + { + *hdfPath = (*argIt).GetP(0); + } + else + { + printf("[WARN] Launcher: Ignoring duplicate input file " + "argument.\n"); + } + } + + if (currentArg == argKeys["gdb"]) + { + currentProcess = (*argIt).GetP(0); + if (currentProcess.empty()) + { + printf("[WARN] Launcher: GDB process argument empty. " + "Ignoring.\n"); + } + + /* If it's not a valid process, we panic. */ + if (gdbProcs->find(currentProcess) == gdbProcs->end()) + { + printf("[WARN] Launcher: Gdb process argument '%s' does not " + "correspond to an Orchestrator executable.\n", + currentProcess.c_str()); + } + + /* If it is, it's true! */ + else (*gdbProcs)[currentProcess] = true; + } + + if (currentArg == argKeys["help"]) /* Help: Print and leave. */ + { + printf("%s", helpDoc.c_str()); + return 1; + } + + if (currentArg == argKeys["noMotherships"]) + { + if (*useMotherships) + { + *useMotherships = false; + } + else + { + printf("[WARN] Launcher: Ignoring duplicate noMotherships " + "argument.\n"); + } + } + + if (currentArg == argKeys["override"]) /* Override: Store it. */ + { + if (overrideHost->empty()) + { + *overrideHost = (*argIt).GetP(0); + if (overrideHost->empty()) + { + printf("[WARN] Launcher: Override host argument empty. " + "Ignoring.\n"); + } + } + else + { + printf("[WARN] Launcher: Ignoring duplicate override" + "argument.\n"); + } + } + + if (currentArg == argKeys["internalPath"]) + { + if (internalPath->empty()) + { + *internalPath = (*argIt).GetP(0); + if (internalPath->empty()) + { + printf("[WARN] Launcher: Internal path argument empty. " + "Ignoring.\n"); + } + } + else + { + printf("[WARN] Launcher: Ignoring duplicate internal path " + "argument.\n"); + } + } + + if (currentArg == argKeys["valgrind"]) + { + currentProcess = (*argIt).GetP(0); + if (currentProcess.empty()) + { + printf("[WARN] Launcher: Valgrind process argument empty. " + "Ignoring.\n"); + } + + /* If it's not a valid process, we panic. */ + if (valgrindProcs->find(currentProcess) == valgrindProcs->end()) + { + printf("[WARN] Launcher: Valgrind process argument '%s' does " + "not correspond to an Orchestrator executable.\n", + currentProcess.c_str()); + } + + /* If it is, it's true! */ + else (*valgrindProcs)[currentProcess] = true; + } + } + + /* Print what happened, if anyone is listening. */ + #if ORCHESTRATOR_DEBUG + DebugPrint("%sParsed arguments:\n", debugHeader); + if (hdfPath->empty()) + { + DebugPrint("%s%sHardware description file path: Not specified.\n", + debugHeader, debugIndent); + } + else + { + DebugPrint("%s%sHardware description file path: %s\n", + debugHeader, debugIndent, hdfPath->c_str()); + } + if (overrideHost->empty()) + { + DebugPrint("%s%sOverride host: Not specified.\n", + debugHeader, debugIndent); + } + else + { + DebugPrint("%s%sOverride host: %s\n", debugHeader, debugIndent, + overrideHost->c_str()); + } + if (*useMotherships) + { + DebugPrint("%s%sMotherships: enabled\n", debugHeader, debugIndent); + } + else + { + DebugPrint("%s%sMotherships: disabled\n", debugHeader, debugIndent); + } + if (*dryRun) + { + DebugPrint("%s%sWe're not actually going to run the command.\n", + debugHeader, debugIndent); + } + else + { + DebugPrint("%s%sWe are going to run the generated command.\n", + debugHeader, debugIndent); + } + + DebugPrint("%s\n", debugHeader); + #endif + + return 0; +} +} /* End namespace */ diff --git a/Source/Launcher/LauncherMain.h b/Source/Launcher/LauncherMain.h new file mode 100644 index 00000000..4c26e55d --- /dev/null +++ b/Source/Launcher/LauncherMain.h @@ -0,0 +1,57 @@ +#ifndef __ORCHESTRATOR_SOURCE_LAUNCHER_LAUNCHERMAIN_H +#define __ORCHESTRATOR_SOURCE_LAUNCHER_LAUNCHERMAIN_H + +#include "Cli.h" +#include "Debug.h" +#include "jnj.h" +#include "macros.h" +#include "OSFixes.hpp" +#include "RootArgs.h" +#include "SSH.h" + +#include +#include +#include +#include + +#include + +namespace Launcher +{ + const char* debugHeader = "[LAUNCHER] "; + const char* debugIndent = " "; + const char* deployDir = ".orchestrator/launcher"; /* No trailing slash, + * relative to home. */ + const char* errorHeader = "[LAUNCHER] [ERROR] "; + const char* fileOnlyOnPoetsBox = "/local/ecad/setup-quartus17v0.bash"; + + const char* defaultHdfPath = "/local/orchestrator-common/hdf.uif"; + + const char* execClock = "rtcl"; + const char* execLogserver = "logserver"; + const char* execMothership = "mothership"; + const char* execRoot = "root"; + + const char* execGdb = "/usr/bin/gdb --args"; + const char* execValgrind = "/usr/bin/valgrind"; + + bool AreWeRunningOnAPoetsBox(); + void BuildCommand(bool useMotherships, std::string internalPath, + std::string overrideHost, std::string batchPath, + std::string hdfPath, + std::set mothershipHosts, + std::map executablePaths, + std::string* command); + int DeployBinaries(std::set* hosts, + std::map* paths); + int GetHosts(std::string hdfPath, std::set* hosts); + int Launch(int argc, char** argv); + int ParseArgs(int argc, char** argv, std::string* batchPath, + std::string* hdfPath, bool* useMotherships, bool* dryRun, + std::string* overrideHost, std::string* internalPath, + std::map* gdbProcs, + std::map* valgrindProcs); + +} + +#endif diff --git a/Source/Launcher/SSH.cpp b/Source/Launcher/SSH.cpp new file mode 100644 index 00000000..4f4b22a7 --- /dev/null +++ b/Source/Launcher/SSH.cpp @@ -0,0 +1,56 @@ +#include "SSH.h" + +/* Runs an SSH command, returning the exit code. + * + * This function opens a single SSH session, and runs commands natively in the + * shell configured in the SSH config file, falling back to the SHELL + * environment variable on the host (usually /bin/bash). + * + * Beware, all of the caveats with Call::call apply here. + * + * Arguments: + * + * - host: host to connect to (see SSH.h). + * - command: command to run, passed naively to the shell. + * - stdout: string populated with the contents of the standard output. + * - error: string populated with the contents of the standard error, or other + * connection errors. + * + * Returns the exit code of the called process. */ +int SSH::call(std::string host, std::string command, std::string* stdout, + std::string* stderr) +{ + /* Build the full command. */ + std::vector sshCommand; + sshCommand.push_back(SSH_COMMAND); + sshCommand.push_back(host); + sshCommand.push_back("--"); + sshCommand.push_back(command); + + /* Run it. */ + return Call::call(sshCommand, stdout, stderr); +} + +/* Deploys a directory to a host over SSH with many caveats. Arguments: + * + * - host: host to connect to (see SSH.h). + * - source: Path to the directory whose contents are to be copied, assumed to + * exist. Must not have a trailing slash. + * - target: Path to the directory to populate on the host, assumed to + * exist. Must not have a trailing slash. + * + * Returns 0 on success, and 1 on failure. */ +int SSH::deploy_directory(std::string host, std::string source, + std::string target, std::string* stdout, + std::string* stderr) +{ + /* Build the command */ + std::vector scpCommand; + scpCommand.push_back(SCP_COMMAND); + scpCommand.push_back(RECURSIVE); + scpCommand.push_back(source); + scpCommand.push_back(host + ":~/" + target); + + /* Run it. */ + return Call::call(scpCommand, stdout, stderr); +} diff --git a/Source/Launcher/SSH.h b/Source/Launcher/SSH.h new file mode 100644 index 00000000..5c0c4f8d --- /dev/null +++ b/Source/Launcher/SSH.h @@ -0,0 +1,44 @@ +#ifndef __ORCHESTRATOR_SOURCE_LAUNCHER_SSH_H +#define __ORCHESTRATOR_SOURCE_LAUNCHER_SSH_H + +/* A simple library to perform SSH commands. Probably won't work on Windows. + * + * The "host" argument in each of the SSH methods corresponds to a target that + * can be reached over SSH without user intervention. This usually means that: + * + * - A "User" is defined in the SSH configuration for this host, if it is + * different from the user on this machine. + * + * - There is a secure keypair that permits the connection, or that an + * SSH-agent of some kind is running. + * + * - The host is known (i.e. is registered in known_hosts). + * + * The "right way" to do this is via something like libssh, but: + * + * - It's going to take me some time for me to learn how to use it. + * + * - The C++ API is not exactly well documented. + * + * - The functionality provided by this library is going to be replaced by a + * NFS eventually anyway. */ + +#include "Call.h" + +#include +#include + +#define RECURSIVE "-r" +#define SCP_COMMAND "/usr/bin/scp" +#define SSH_COMMAND "/usr/bin/ssh" + +namespace SSH +{ + int call(std::string host, std::string command, std::string* stdout, + std::string* stderr); + int deploy_directory(std::string host, std::string source, + std::string target, std::string* stdout, + std::string* stderr); +} + +#endif diff --git a/Source/LogServer/LogServer.cpp b/Source/LogServer/LogServer.cpp index a8086f09..44ad5eff 100644 --- a/Source/LogServer/LogServer.cpp +++ b/Source/LogServer/LogServer.cpp @@ -8,6 +8,8 @@ #include "jnj.h" #include +#include "OSFixes.hpp" + //============================================================================== LogServer::LogServer(int argc,char * argv[],string d): @@ -113,7 +115,20 @@ printf("Key Method\n"); WALKVECTOR(FnMap_t*,FnMapx,F) { WALKMAP(unsigned,pMeth,(**F),i) - fprintf(fp,"%#010x %#016x\n",(*i).first,(*i).second); +{ + //fprintf(fp,"%#010x 0x%#016x\n",(*i).first,(*i).second); + fprintf(fp,"%#010x ",(*i).first); + + // Now for a horrible double type cast to get us a sensible function pointer. + // void*s are only meant to point to objects, not functions. So we get to a + // void** as a pointer to a function pointer is an object pointer. We can then + // follow this pointer to get to the void*, which we then reinterpret to get + // the function's address as a uint64_t. + fprintf(fp,"%" PTR_FMT "\n",reinterpret_cast( + *(reinterpret_cast(&((*i).second)))) + ); +} + } printf("\nMessage map:\n"); printf("Key(id) Data(id : format string)\n"); diff --git a/Source/LogServer/LogServerMain.cpp b/Source/LogServer/LogServerMain.cpp index 602dc447..40242270 100644 --- a/Source/LogServer/LogServerMain.cpp +++ b/Source/LogServer/LogServerMain.cpp @@ -13,11 +13,11 @@ LogServer * pLogServer = 0; try { pLogServer = new LogServer(argc,argv,string(csLOGSERVERproc)); } -catch(bad_alloc) { +catch(bad_alloc&) { printf("\n\n%s Main out of memory... \n\n",csLOGSERVERproc); fflush(stdout); } -catch(Unrec_t u) { +catch(Unrec_t& u) { u.Post(); } catch(...) { diff --git a/Source/Mothership/TMoth.cpp b/Source/Mothership/Mothership.cpp similarity index 64% rename from Source/Mothership/TMoth.cpp rename to Source/Mothership/Mothership.cpp index eef828b8..47ba9878 100644 --- a/Source/Mothership/TMoth.cpp +++ b/Source/Mothership/Mothership.cpp @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ -#include "TMoth.h" +#include "Mothership.h" #include #include #include @@ -10,40 +10,60 @@ #include "string.h" #include "limits.h" -const int TMoth::NumBoards; +const int Mothership::NumBoards; //============================================================================== -TMoth::TMoth(int argc,char * argv[],string d) : - CommonBase(argc,argv,d,string(__FILE__)), HostLink() +Mothership::Mothership(int argc,char * argv[],string d) : + SBase(argc,argv,d,string(__FILE__)), HostLink() { FnMapx.push_back(new FnMap_t); // create a new event map in the derived class // Load the incoming event map -(*FnMapx[0])[PMsg_p::KEY(Q::EXIT )] = &TMoth::OnExit; // overloads CommonBase -(*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &TMoth::OnCmnd; -(*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &TMoth::OnCmnd; -(*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &TMoth::OnCmnd; -(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DIST )] = &TMoth::OnName; -(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::RECL )] = &TMoth::OnName; -(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::TDIR )] = &TMoth::OnName; -(*FnMapx[0])[PMsg_p::KEY(Q::SUPR )] = &TMoth::OnSuper; -(*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &TMoth::OnSyst; -(*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &TMoth::OnSyst; -(*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &TMoth::OnSyst; -(*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &TMoth::OnSyst; -(*FnMapx[0])[PMsg_p::KEY(Q::TINS )] = &TMoth::OnTinsel; - -// mothership's address in POETS space is the host thread ID: X coordinate 0, -// Y coordinate max. -PAddress = TinselMeshYLen << (TinselMeshXBits+TinselLogCoresPerBoard+TinselLogThreadsPerCore); +(*FnMapx[0])[PMsg_p::KEY(Q::EXIT )] = &Mothership::OnExit; // overloads CommonBase +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &Mothership::OnCfg; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &Mothership::OnCfg; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &Mothership::OnCfg; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &Mothership::OnCfg; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &Mothership::OnCfg; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &Mothership::OnCfg; +(*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &Mothership::OnCmnd; +(*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &Mothership::OnCmnd; +(*FnMapx[0])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &Mothership::OnCmnd; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &Mothership::OnDump; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &Mothership::OnDump; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &Mothership::OnDump; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NM )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ID )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ALL )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NGRP)] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::IGRP)] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NSUP)] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ISUP)] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::SUPV )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::EXTN )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::NM )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::IN )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::OUT )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::ATTR )] = &Mothership::OnSend; +(*FnMapx[0])[PMsg_p::KEY(Q::SUPR )] = &Mothership::OnSuper; +(*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &Mothership::OnSyst; +(*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &Mothership::OnSyst; +(*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &Mothership::OnSyst; +(*FnMapx[0])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &Mothership::OnSyst; +(*FnMapx[0])[PMsg_p::KEY(Q::TINS )] = &Mothership::OnTinsel; + +// mothership's address in POETS space is the host bridge ID: 10 | BoxXCoordinate << +// number of bits in the Tinsel address space. The version below only deals with +// left-hand-side boxes. Later this should be fixed. +PAddress = 1 << (1+TinselMeshYBits+TinselMeshXBits+TinselLogCoresPerBoard+TinselLogThreadsPerCore); twig_running = false; ForwardMsgs = false; // don't forward tinsel traffic yet -MPISpinner(); // Spin on *all* messages; exit on DIE +MPISpinner(); // Spin on *all* messages; exit on DIE DebugPrint("Exiting Mothership. Closedown flags: AcceptConns: %s, " "ForwardMsgs: %s\n", AcceptConns ? "true" : "false", ForwardMsgs ? "true" : "false"); @@ -53,9 +73,9 @@ printf("********* Mothership rank %d on the way out\n",Urank); fflush(stdout); //------------------------------------------------------------------------------ -TMoth::~TMoth() +Mothership::~Mothership() { -//printf("********* Mothership rank %d destructor\n",Urank); fflush(stdout); +DebugPrint("********* Mothership rank %d destructor\n",Urank); WALKMAP(uint32_t,PinBuf_t*,TwigMap,D) { if (D->second) @@ -74,7 +94,7 @@ WALKVECTOR(FnMap_t*,FnMapx,F) //------------------------------------------------------------------------------ -unsigned TMoth::Boot(string task) +unsigned Mothership::Boot(string task) { DebugPrint("Entering boot stage\n"); if (TaskMap.find(task) == TaskMap.end()) @@ -106,9 +126,9 @@ unsigned TMoth::Boot(string task) if (numThreads) { (*C)->get_hardware_address()->populate_a_software_address(&coreAddress); - t_start_bitmap[TMoth::GetHWAddr(coreAddress)] = new vector(numThreads/(8*sizeof(unsigned)),UINT_MAX); + t_start_bitmap[Mothership::GetHWAddr(coreAddress)] = new vector(numThreads/(8*sizeof(unsigned)),UINT_MAX); unsigned remainder = numThreads%(8*sizeof(unsigned)); - if (remainder) t_start_bitmap[TMoth::GetHWAddr(coreAddress)]->push_back(UINT_MAX >> ((8*sizeof(unsigned))-remainder)); + if (remainder) t_start_bitmap[Mothership::GetHWAddr(coreAddress)]->push_back(UINT_MAX >> ((8*sizeof(unsigned))-remainder)); } } DebugPrint("Task start bitmaps created for %d cores\n", t_start_bitmap.size()); @@ -116,18 +136,18 @@ unsigned TMoth::Boot(string task) WALKVECTOR(P_core*,taskCores,C) { (*C)->get_hardware_address()->populate_a_software_address(&coreAddress); - fromAddr(TMoth::GetHWAddr(coreAddress),&mX,&mY,&core,&thread); - DebugPrint("Booting %d threads on threadID 0x%X at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),TMoth::GetHWAddr(coreAddress),mX,mY,core); + fromAddr(Mothership::GetHWAddr(coreAddress),&mX,&mY,&core,&thread); + DebugPrint("Booting %d threads on threadID 0x%X at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress),mX,mY,core); startOne(mX,mY,core,(*C)->P_threadm.size()); - DebugPrint("%d threads started on threadID 0x%X at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),TMoth::GetHWAddr(coreAddress),mX,mY,core); - DebugPrint("Triggering %d threads on core %d at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),TMoth::GetHWAddr(coreAddress),mX,mY,core); + DebugPrint("%d threads started on threadID 0x%X at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress),mX,mY,core); + DebugPrint("Triggering %d threads on core %d at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress),mX,mY,core); goOne(mX,mY,core); } // per Matt Naylor comment safer to start all cores then issue the go command to all cores separately. // WALKVECTOR(P_core*,taskCores,C) // { // (*C)->get_hardware_address()->populate_a_software_address(&coreAddress); - // DebugPrint("Triggering %d threads on core %d at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),TMoth::GetHWAddr(coreAddress),mX,mY,core); + // DebugPrint("Triggering %d threads on core %d at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress),mX,mY,core); // goOne(mX,mY,core); // } // DebugPrint("%d cores booted\n", taskCores.size()); @@ -174,8 +194,10 @@ unsigned TMoth::Boot(string task) else { Post (540,int2str(Urank)); + //int (*SupervisorInit)(CommonBase*) = reinterpret_cast(dlsym(SuperHandle, "SupervisorInit")); int (*SupervisorInit)() = reinterpret_cast(dlsym(SuperHandle, "SupervisorInit")); string badFunc(""); + //if (!SupervisorInit || (*SupervisorInit)(this)) badFunc = "SupervisorInit"; if (!SupervisorInit || (*SupervisorInit)()) badFunc = "SupervisorInit"; else if ((SupervisorCall = reinterpret_cast(dlsym(SuperHandle, "SupervisorCall"))) == NULL) badFunc = "SupervisorCall"; if (badFunc.size()) Post(533,badFunc,int2str(Urank),string(dlerror())); @@ -188,6 +210,7 @@ unsigned TMoth::Boot(string task) Post(531, int2str(Urank)); } else twig_running =true; + Post(561, task.c_str()); TaskMap[task]->status = TaskInfo_t::TASK_BARR; // now at barrier on the tinsel side. return 0; } @@ -204,7 +227,7 @@ unsigned TMoth::Boot(string task) //------------------------------------------------------------------------------ -unsigned TMoth::CmLoad(string task) +unsigned Mothership::CmLoad(string task) // Load a task to the system { DebugPrint("%d tasks deployed to Mothership\n", TaskMap.size()); @@ -271,6 +294,7 @@ unsigned TMoth::CmLoad(string task) } TaskMap[task]->status = TaskInfo_t::TASK_RDY; DebugPrint("Task status is TASK_RDY\n"); + Post(560,task); // boot the board (which has to be done from the main thread as it is not // thread-safe) return Boot(task); @@ -278,7 +302,7 @@ unsigned TMoth::CmLoad(string task) //------------------------------------------------------------------------------ -unsigned TMoth::CmRun(string task) +unsigned Mothership::CmRun(string task) // Run a specified task. This passes the tinsel-side barrier to start // application execution { @@ -297,10 +321,13 @@ unsigned TMoth::CmRun(string task) return 1; } case TaskInfo_t::TASK_STOP: + Post(512, task,"run","TASK_STOP"); + TaskMap[task]->status = TaskInfo_t::TASK_ERR; + return 2; case TaskInfo_t::TASK_END: - break; + case TaskInfo_t::TASK_IDLE: case TaskInfo_t::TASK_RUN: - Post(511, task,"run","TASK_RUN"); + Post(511, task,"run",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); return 0; case TaskInfo_t::TASK_BARR: { @@ -317,7 +344,7 @@ unsigned TMoth::CmRun(string task) WALKVECTOR(P_thread*,TaskMap[task]->ThreadsForTask(),R) { (*R)->get_hardware_address()->populate_a_software_address(&threadAddress); - threadsToRelease.push_back(TMoth::GetHWAddr(threadAddress)); + threadsToRelease.push_back(Mothership::GetHWAddr(threadAddress)); } DebugPrint("Issuing barrier release to %d threads in task %s, using " "message address 0x%X\n", threadsToRelease.size(), @@ -343,7 +370,7 @@ unsigned TMoth::CmRun(string task) //------------------------------------------------------------------------------ -unsigned TMoth::CmStop(string task) +unsigned Mothership::CmStop(string task) // Handle a stop command (which ends the simulation) { if (TaskMap.find(task) == TaskMap.end()) @@ -354,11 +381,14 @@ unsigned TMoth::CmStop(string task) switch(TaskMap[task]->status) { case TaskInfo_t::TASK_IDLE: + case TaskInfo_t::TASK_END: + Post(511, task,"stopped",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + return 0; case TaskInfo_t::TASK_BOOT: - break; case TaskInfo_t::TASK_RDY: + case TaskInfo_t::TASK_STOP: { - Post(813, task, "TASK_RDY"); + Post(813, task, "stopped",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); return 1; } case TaskInfo_t::TASK_BARR: @@ -388,7 +418,7 @@ unsigned TMoth::CmStop(string task) WALKVECTOR(P_thread*, TaskMap[task]->ThreadsForTask(), R) { (*R)->get_hardware_address()->populate_a_software_address(&threadAddress); - uint32_t destDevAddr = TMoth::GetHWAddr(threadAddress); + uint32_t destDevAddr = Mothership::GetHWAddr(threadAddress); DebugPrint("Stopping thread %d in task %s\n", destDevAddr, task.c_str()); stop_msg.destDeviceAddr = DEST_BROADCAST; // issue the stop message to all devices // then issue the stop packet @@ -402,9 +432,6 @@ unsigned TMoth::CmStop(string task) if (twig_running) StopTwig(); return 0; } - case TaskInfo_t::TASK_STOP: - case TaskInfo_t::TASK_END: - break; default: TaskMap[task]->status = TaskInfo_t::TASK_ERR; } @@ -414,7 +441,180 @@ unsigned TMoth::CmStop(string task) //------------------------------------------------------------------------------ -void TMoth::Dump(FILE * fp) +unsigned Mothership::ConfigDistribute(PMsg_p* msg, unsigned comm) +// receive a broadcast core info block from the NameServer. +{ + CMsg_p task_info(*msg); + string TaskName = msg->Zname(0); + map::iterator T; + if ((T=TaskMap.find(TaskName)) == TaskMap.end()) + { + DebugPrint("Inserting new task %s from NameDist\n", TaskName.c_str()); + TaskMap[TaskName] = new TaskInfo_t(TaskName); + } + DebugPrint("Task %s will have binaries in directory %s\n",msg->Zname(0).c_str(),msg->Zname(1).c_str()); + vector> cores; + task_info.Get(cores); + DebugPrint("Task %s has %d cores\n",TaskName.c_str(),cores.size()); + // set up the cores + for (vector>::iterator core = cores.begin(); core != cores.end(); core++) + TaskMap[TaskName]->insertCore(core->first, core->second); + DebugPrint("%d cores inserted into TaskInfo structure for %s\n",cores.size(),TaskName.c_str()); + unsigned err = AddressBook::SUCCESS; + if ((err = SBase::ConfigDistribute(msg, comm)) != AddressBook::SUCCESS) return err; + DebugPrint("About to set binary directory %s for task %s\n",msg->Zname(1).c_str(),msg->Zname(0).c_str()); + return ConfigDir(msg, comm); // distribute message has the directory also +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::ConfigRecall(PMsg_p* msg, unsigned comm) +// remove (recall) a core info block from the Mothership. +{ + string TaskName = msg->Zname(0); + map::iterator T; + vector taskNames; // list of tasks to recall + if (TaskName.empty()) // no task name => recall all tasks + { + WALKMAP(string, TaskInfo_t*, TaskMap, task) + taskNames.push_back(task->first); + } + else // otherwise remove the named task + { + if ((T=TaskMap.find(TaskName)) == TaskMap.end()) // task exists? + { + Post(607, TaskName); // No. Nothing to do in this Mothership. + return SBase::ConfigRecall(msg, comm); // but remove from base. + } + taskNames.push_back(TaskName); + } + WALKVECTOR(string, taskNames, tName) // recall all tasks in the list + { + switch (TaskMap[*tName]->status) + { + case TaskInfo_t::TASK_IDLE: + case TaskInfo_t::TASK_BOOT: + case TaskInfo_t::TASK_STOP: + case TaskInfo_t::TASK_END: + break; + case TaskInfo_t::TASK_RDY: + { + // A task that is ready to run should not be recalled + // but stopped first. + Post(813, *tName, "recalled", "TASK_RDY"); + return AddressBook::ERR_INVALID_TASK; + } + case TaskInfo_t::TASK_BARR: + case TaskInfo_t::TASK_RUN: + CmStop(*tName); // stop any running tasks before recalling them. + break; + default: + { + // Task in an unknown state - something very wrong has happened! + TaskMap[*tName]->status = TaskInfo_t::TASK_ERR; + Post(812, *tName); + return AddressBook::ERR_INVALID_TASK; + } + } // ends switch (TaskMap[*tName]->status) + system((string("rm -r -f ")+TaskMap[*tName]->BinPath).c_str()); + delete TaskMap[*tName]; // get rid of its TaskInfo object + TaskMap.erase(*tName); // and then remove it from the task map + } + return SBase::ConfigRecall(msg, comm); // remove from base class +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::ConfigDir(PMsg_p *msg, unsigned comm) +// set the path where binaries for a given task are to be found +{ + string task = msg->Zname(0); + string dir = msg->Zname(1); + DebugPrint("Name config dir command received: task %s, directory %s\n", task.c_str(), dir.c_str()); + map::iterator T; + if ((T=TaskMap.find(task)) == TaskMap.end()) + { + DebugPrint("Inserting new task %s from NameTdir\n", task.c_str()); + TaskMap[task] = new TaskInfo_t(task); + } + TaskMap[task]->BinPath = dir; + return SBase::ConfigDir(msg, comm); +} + +//------------------------------------------------------------------------------ + +// A Mothership should never receive a state-configuration name message. It +// should only send them, or update the state internally and call the SBase +// method from the internal update. +unsigned Mothership::ConfigState(PMsg_p *msg, unsigned comm) +{ + // get the name to distinguish the type of error + string taskName = msg->Zname(0); + if (taskName.empty()) + Post(710, "ConfigState", int2str(Urank)); // no task name + else if (TaskMap.find(taskName) == TaskMap.end()) + Post(715, taskName, int2str(Urank)); // task not loaded to this Mothership + else + { + map::const_iterator st = TaskInfo_t::Task_Status.find(TaskMap[taskName]->status); + if (st == TaskInfo_t::Task_Status.end()) + { + Post(550, int2str(Urank), msg->Zname(1), "UNKNOWN"); + return AddressBook::ERR_INVALID_STATE; + } + else Post(550, int2str(Urank), msg->Zname(1), st->second); + } + return AddressBook::ERR_NONFATAL; // whatever the case we ignore the message +} + +//------------------------------------------------------------------------------ + +unsigned Mothership::Connect(string svc) +{ + unsigned connErr = MPI_SUCCESS; + // set up the connection in the base class + if ((connErr = SBase::Connect(svc)) != MPI_SUCCESS) return connErr; + FnMapx.push_back(new FnMap_t); // add another function table in the derived class + int fIdx=FnMapx.size()-1; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::EXIT )] = &Mothership::OnExit; // overloads CommonBase + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DIST )] = &Mothership::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::TDIR )] = &Mothership::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::BLD )] = &Mothership::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::RECL )] = &Mothership::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::DEL )] = &Mothership::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::CFG,Q::STATE )] = &Mothership::OnCfg; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::LOAD )] = &Mothership::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::RUN )] = &Mothership::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::CMND,Q::STOP )] = &Mothership::OnCmnd; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::ALL )] = &Mothership::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::TASK,Q::NM )] = &Mothership::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::DUMP,Q::LIST )] = &Mothership::OnDump; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NM )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ID )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ALL )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NGRP)] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::IGRP)] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::NSUP)] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVI,Q::ISUP)] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::SUPV )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::EXTN )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::NM )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::IN )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::DEVT,Q::OUT )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::NAME,Q::SEND,Q::ATTR )] = &Mothership::OnSend; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SUPR )] = &Mothership::OnSuper; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::HARD )] = &Mothership::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::KILL )] = &Mothership::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::SHOW )] = &Mothership::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::SYST,Q::TOPO )] = &Mothership::OnSyst; + (*FnMapx[fIdx])[PMsg_p::KEY(Q::TINS )] = &Mothership::OnTinsel; + return MPI_SUCCESS; +} + + +//------------------------------------------------------------------------------ + +void Mothership::Dump(FILE * fp, string task) { fprintf(fp,"Mothership dump+++++++++++++++++++++++++++++++++++\n"); fprintf(fp,"Event handler table:\n"); @@ -427,14 +627,41 @@ WALKMAP(unsigned,pMeth,(**F),i) fprintf(fp,"%#010x %018lx\n",(*i).first,(uint64_t)&(*i).second); //0x%010x } fprintf(fp,"Loaded tasks:\n"); -WALKMAP(string,TaskInfo_t*,TaskMap,Task) +WALKMAP(string,TaskInfo_t*,TaskMap,TaskI) { - fprintf(fp,"Task %s in state %s:\n",Task->second->TaskName.c_str(),TaskInfo_t::Task_Status.find(Task->second->status)->second.c_str()); - fprintf(fp,"Reading from binary path %s\n",Task->second->BinPath.c_str()); + fprintf(fp,"Task %s in state %s:\n",TaskI->second->TaskName.c_str(),TaskInfo_t::Task_Status.find(TaskI->second->status)->second.c_str()); + fprintf(fp,"Reading from binary path %s\n",TaskI->second->BinPath.c_str()); fprintf(fp,"Task map dump:\n"); - Task->second->VirtualBox->Dump(fp); + TaskI->second->VirtualBox->Dump(fp); fprintf(fp,".......................................\n"); } +if (task.empty()) +{ + fprintf(fp,"SBase summary dump___________________________\n"); + SBase::Dump(fp); + fprintf(fp,"SBase summary dump---------------------------\n"); +} +else if (task == "*") +{ + vector tasks; + unsigned err = AddressBook::SUCCESS; + if ((err = ListTask(tasks))) // get all the tasks + { + Post(710, "Dump", int2str(Urank)); // error: no tasks were found + if (fp != stdout) fclose(fp); + return; // so we don't need to do anything + } + fprintf(fp,"SBase dump of all tasks______________________\n"); + WALKVECTOR(string, tasks, task) SBase::Dump(fp, *task); // dump it all out + fprintf(fp,"SBase dump of all tasks----------------------\n"); +} +else +{ + fprintf(fp,"SBase dump___________________________________\n"); + SBase::Dump(fp,task); + fprintf(fp,"SBase dump-----------------------------------\n"); +} + fprintf(fp,"Mothership dump-----------------------------------\n"); fflush(fp); CommonBase::Dump(fp); @@ -442,7 +669,41 @@ CommonBase::Dump(fp); //------------------------------------------------------------------------------ -long TMoth::LoadBoard(P_board* board) +// Name Dump responses (from -n and -s switches) + +unsigned Mothership::DumpAll(PMsg_p * msg, unsigned comm) +{ + /* Second static name string is the file name to dump to. The first static + string should be empty. We use the second one so that the first field is + used in a consistent way - for a task name. Filename must be treated as an + absolute literal, because tasks could in + general have different paths, therefore there is no way to infer what + additional path string might be appended to the file name given. + */ + string filename = msg->Zname(1); + FILE* dumpFile; + if (filename.empty()) dumpFile = stdout; // empty filename => dump to console + else dumpFile = fopen(filename.c_str(), "a"); + Dump(dumpFile, "*"); + if (dumpFile != stdout) fclose(dumpFile); + return SUCCESS; // all tasks dump can't fail badly +} + +unsigned Mothership::DumpSummary(PMsg_p * msg, unsigned comm) +{ + // route through base to arrive (virtually) back at the Dump command + // above + return SBase::DumpSummary(msg, comm); +} + +unsigned Mothership::DumpTask(PMsg_p * msg, unsigned comm) +{ + return SBase::DumpTask(msg, comm); +} + +//------------------------------------------------------------------------------ + +long Mothership::LoadBoard(P_board* board) { long coresLoaded = 0; string task; @@ -451,6 +712,7 @@ long TMoth::LoadBoard(P_board* board) WALKMAP(string, TaskInfo_t*, TaskMap, K) { if (K->second->status == TaskInfo_t::TASK_BOOT) task = K->first; + DebugPrint("Found task to load %s\n", task.c_str()); break; } if (!task.empty()) // anything to do? @@ -459,6 +721,7 @@ long TMoth::LoadBoard(P_board* board) P_addr coreAddress; uint32_t mX, mY, core, thread; // Intermediates for HostLink-side // address components. + DebugPrint("Loading task %s\n", task.c_str()); WALKPDIGRAPHNODES(AddressComponent, P_mailbox*, unsigned, P_link*, unsigned, P_port*, board->G, MB) @@ -481,13 +744,13 @@ long TMoth::LoadBoard(P_board* board) C->second->get_hardware_address()-> populate_a_software_address(&coreAddress); DebugPrint("Loading core with virtual address Bx:%d, Bd:%d, " - "Cr:%d\n", + "Mb: %d, Cr:%d\n", coreAddress.A_box, coreAddress.A_board, - coreAddress.A_core); - fromAddr(TMoth::GetHWAddr(coreAddress), &mX, &mY, &core, + coreAddress.A_mailbox, coreAddress.A_core); + fromAddr(Mothership::GetHWAddr(coreAddress), &mX, &mY, &core, &thread); DebugPrint("Loading hardware thread 0x%X at x:%d y:%d c:%d\n", - TMoth::GetHWAddr(coreAddress), mX, mY, core); + Mothership::GetHWAddr(coreAddress), mX, mY, core); // Load instruction memory, then data memory. loadInstrsOntoCore(code_f.c_str(), mX, mY, core); @@ -501,90 +764,34 @@ long TMoth::LoadBoard(P_board* board) return coresLoaded; } -//------------------------------------------------------------------------------ - -unsigned TMoth::NameDist(PMsg_p* mTask_Info) -// receive a broadcast core info block from the NameServer. -{ - CMsg_p task_info(*mTask_Info); - string TaskName; - task_info.Get(0, TaskName); - map::iterator T; - if ((T=TaskMap.find(TaskName)) == TaskMap.end()) - { - DebugPrint("Inserting new task %s from NameDist\n", TaskName.c_str()); - TaskMap[TaskName] = new TaskInfo_t(TaskName); - } - vector> cores; - task_info.Get(cores); - DebugPrint("Task %s has %d cores\n",TaskName.c_str(),cores.size()); - // set up the cores - for (vector>::iterator core = cores.begin(); core != cores.end(); core++) - TaskMap[TaskName]->insertCore(core->first, core->second); - DebugPrint("%d cores inserted into TaskInfo structure for %s\n",cores.size(),TaskName.c_str()); - return 0; -} - -//------------------------------------------------------------------------------ - -unsigned TMoth::NameRecl(PMsg_p* mTask_Info) -// remove (recall) a core info block from the Mothership. -{ - string TaskName; - mTask_Info->Get(0, TaskName); - map::iterator T; - if ((T=TaskMap.find(TaskName)) == TaskMap.end()) // task exists? - { - Post(607, TaskName); // No. Nothing to do. - return 0; - } - switch (TaskMap[TaskName]->status) - { - case TaskInfo_t::TASK_IDLE: - case TaskInfo_t::TASK_BOOT: - case TaskInfo_t::TASK_STOP: - case TaskInfo_t::TASK_END: - break; - case TaskInfo_t::TASK_RDY: - { - Post(813, TaskName, "recalled", "TASK_RDY"); - return 1; - } - case TaskInfo_t::TASK_BARR: - case TaskInfo_t::TASK_RUN: - CmStop(TaskName); // stop any running tasks before recalling them. - break; - default: - { - TaskMap[TaskName]->status = TaskInfo_t::TASK_ERR; - Post(812, TaskName); - return 1; - } - } - system((string("rm -r -f ")+TaskMap[TaskName]->BinPath).c_str()); - delete T->second; // get rid of its TaskInfo object - TaskMap.erase(T); // and then remove it from the task map - return 0; -} //------------------------------------------------------------------------------ -unsigned TMoth::NameTdir(const string& task, const string& dir) -// set the path where binaries for a given task are to be found +unsigned Mothership::OnCfg(PMsg_p *msg, unsigned comm) { - map::iterator T; - if ((T=TaskMap.find(task)) == TaskMap.end()) - { - DebugPrint("Inserting new task %s from NameTdir\n", task.c_str()); - TaskMap[task] = new TaskInfo_t(task); - } - TaskMap[task]->BinPath = dir; - return 0; + switch(msg->L(2)) + { + case Q::DIST: + return ConfigDistribute(msg, comm); + case Q::TDIR: + return ConfigDir(msg, comm); + case Q::BLD: + return SBase::ConfigBuild(msg, comm); + case Q::RECL: + return ConfigRecall(msg, comm); + case Q::DEL: + return SBase::ConfigDelete(msg, comm); + case Q::STATE: + return ConfigState(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } } //------------------------------------------------------------------------------ -unsigned TMoth::OnCmnd(PMsg_p * Z, unsigned cIdx) +unsigned Mothership::OnCmnd(PMsg_p * Z, unsigned cIdx) // Handler for a task command sent (probably) from the user. { // get the task that the command is going to operate on @@ -604,115 +811,46 @@ return 0; //------------------------------------------------------------------------------ -void* TMoth::Twig(void* par) -// the Twig processing deals with receiving packets from the managed Tinsel cores. -// Ideally, Twig, OnIdle, and OnTinselOut would reside in a separate *process*. -// For the moment we run Twig in a separate *thread* because to run a separate -// process would require identifying the matching Branch's rank, which could be -// done, using a variety of methods, but none of them trivial, and so that's -// 'for later'. +unsigned Mothership::OnDump(PMsg_p *msg, unsigned comm) { - TMoth* parent = static_cast(par); - const uint32_t szFlit = (1<(recv_buf); - FILE* OutFile; - char Line[4*P_MSG_MAX_SIZE]; - fpos_t readPos; - fpos_t writePos; - if ( (OutFile = fopen("./DebugOutput.txt", "a+")) ) - { - fsetpos(OutFile, &readPos); - fsetpos(OutFile, &writePos); - } - while (parent->ForwardMsgs) // until told otherwise, - { - // receive all available traffic. Should this be done or only one packet - // and then try again for MPI? We don't expect MPI traffic to be intensive - // and tinsel messsages might be time-critical. - while (parent->canRecv()) - { - DebugPrint("Message received from a Device\n"); - parent->recv(recv_buf); - uint32_t* device = static_cast(p_recv_buf); // get the first word, which will be a device address - if (!(*device & P_SUP_MASK)) // bound for an external? - { - P_Msg_Hdr_t* m_hdr = static_cast(p_recv_buf); - DebugPrint("Message is bound for external device %d\n", m_hdr->destDeviceAddr); - if (parent->TwigExtMap[m_hdr->destDeviceAddr] == 0) - parent->TwigExtMap[m_hdr->destDeviceAddr] = new deque; - if (m_hdr->messageLenBytes > szFlit) - parent->recvMsg(recv_buf+szFlit, m_hdr->messageLenBytes-szFlit); - parent->TwigExtMap[m_hdr->destDeviceAddr]->push_back(*(static_cast(p_recv_buf))); - } - else - { - P_Sup_Hdr_t* s_hdr = static_cast(p_recv_buf); - s_hdr->sourceDeviceAddr ^= P_SUP_MASK; + if (msg->L(2) == Q::LIST) return DumpSummary(msg, comm); + if (msg->L(2) != Q::TASK) + { + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } + switch(msg->L(3)) + { + case Q::ALL: + return DumpAll(msg, comm); + case Q::NM: + return DumpTask(msg, comm); + default: + Post(700,uint2str(msg->Key()),int2str(Urank)); + return 0; + } +} - if (s_hdr->command == P_PKT_MSGTYP_ALIVE) - { - DebugPrint("Thread %d is still alive\n", s_hdr->sourceDeviceAddr >> P_THREAD_OS); - } - else - { - DebugPrint("Message is a Supervisor request from device %d\n", s_hdr->sourceDeviceAddr); - if (parent->TwigMap[s_hdr->sourceDeviceAddr] == 0) // new device talking? - { - DebugPrint("New device %d reporting to Supervisor\n", s_hdr->sourceDeviceAddr); - parent->TwigMap[s_hdr->sourceDeviceAddr] = new PinBuf_t; - } - if ((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] == 0) // inactive pin for the device? - { - DebugPrint("New pin %d for device %d reporting to Supervisor\n", s_hdr->destPin, s_hdr->sourceDeviceAddr); - (*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] = new char[MAX_P_SUP_MSG_BYTES](); - } - P_Sup_Msg_t* recvdMsg = static_cast(static_cast((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin])); - memcpy(recvdMsg+s_hdr->seq,s_hdr,p_sup_hdr_size()); // stuff header into the persistent buffer - DebugPrint("Expecting message of total length %d\n", s_hdr->cmdLenBytes); - uint32_t len = s_hdr->seq == s_hdr->cmdLenBytes/p_sup_msg_size() ? s_hdr->cmdLenBytes%p_sup_msg_size() : p_sup_msg_size(); // more message to receive? - DebugPrint("Length for sequence number %d: %d\n", s_hdr->seq, len); - if (len > szFlit) parent->recvMsg(((recvdMsg+s_hdr->seq)->data), len-szFlit); // get the whole message - if (super_buf_recvd(recvdMsg)) - { - DebugPrint("Entire Supervisor message received of length %d\n", s_hdr->cmdLenBytes); - if (parent->OnTinselOut(recvdMsg)) - parent->Post(530, int2str(parent->Urank)); - super_buf_clr(recvdMsg); - } - } - } - } - // Capture anything happening on the DebugLink - which is text output directed at a file. - bool updated = false; - if (OutFile) - { - fsetpos(OutFile, &writePos); - while (parent->pollStdOut(OutFile)) updated = true; - if (updated) - { - DebugPrint("Received a debug output message\n"); - } - fgetpos(OutFile, &writePos); - } - else while (parent->pollStdOut()); // or possibly only dumped to the local console - // output the debug output buffer, which has to be done immediately because we are in a separate thread - if (OutFile && updated) - { - fflush(OutFile); - fsetpos(OutFile, &readPos); - while (!feof(OutFile)) parent->Post(600, string(fgets(Line, 4*P_MSG_MAX_SIZE, OutFile))); - fgetpos(OutFile, &readPos); - } - } - printf("Exiting Twig thread\n"); - pthread_exit(par); - return par; +//------------------------------------------------------------------------------ + +unsigned Mothership::OnExit(PMsg_p * Z, unsigned cIdx) +// This is what happens when a user command to stop happens +{ +// We are going away. Shut down any active tasks. +WALKMAP(string, TaskInfo_t*, TaskMap, tsk) +{ + if ((tsk->second->status == TaskInfo_t::TASK_BOOT) || (tsk->second->status == TaskInfo_t::TASK_END)) continue; + if (tsk->second->status == TaskInfo_t::TASK_BARR) CmRun(tsk->first); + if (tsk->second->status == TaskInfo_t::TASK_RUN) CmStop(tsk->first); +} +// stop accepting Tinsel messages +if (twig_running) StopTwig(); +return CommonBase::OnExit(Z,cIdx); // exit through CommonBase handler } //------------------------------------------------------------------------------ -void TMoth::OnIdle() +void Mothership::OnIdle() // idle processing deals with forwarding packets from the managed Tinsel cores. { // queues may be changing but we can deal with a static snapshot of the actual @@ -748,54 +886,18 @@ void TMoth::OnIdle() //------------------------------------------------------------------------------ -unsigned TMoth::OnName(PMsg_p * Z, unsigned cIdx) -// This handles what happens when the NameServer dumps a name subblock to the -// mothership -{ -unsigned key = Z->Key(); -if (key == PMsg_p::KEY(Q::NAME,Q::DIST )) -{ - DebugPrint("NameDist command received\n"); - return NameDist(Z); -} -if (key == PMsg_p::KEY(Q::NAME,Q::RECL )) -{ - DebugPrint("NameRecl command received\n"); - return NameRecl(Z); -} -if (key == PMsg_p::KEY(Q::NAME,Q::TDIR )) -{ - string task,dir; - Z->Get(0,task); - Z->Get(1,dir); - DebugPrint("NameTdir command received: task %s, directory %s\n", task.c_str(), dir.c_str()); - return NameTdir(task,dir); -} -else -Post(510,"Name",int2str(Urank)); -return 0; -} - -//------------------------------------------------------------------------------ -unsigned TMoth::OnExit(PMsg_p * Z, unsigned cIdx) -// This is what happens when a user command to stop happens +// OnSend is stubbed for the moment. Will routing like this: though the base +// class, invoke the overloaded virtual functions in the derived Mothership +// class? To be tested when the time comes. +unsigned Mothership::OnSend(PMsg_p *msg, unsigned comm) { -// We are going away. Shut down any active tasks. -WALKMAP(string, TaskInfo_t*, TaskMap, tsk) -{ - if ((tsk->second->status == TaskInfo_t::TASK_BOOT) || (tsk->second->status == TaskInfo_t::TASK_END)) continue; - if (tsk->second->status == TaskInfo_t::TASK_BARR) CmRun(tsk->first); - if (tsk->second->status == TaskInfo_t::TASK_RUN) CmStop(tsk->first); -} -// stop accepting Tinsel messages -if (twig_running) StopTwig(); -return CommonBase::OnExit(Z,cIdx); // exit through CommonBase handler + return SBase::OnSend(msg, comm); } //------------------------------------------------------------------------------ -unsigned TMoth::OnSuper(PMsg_p * Z, unsigned cIdx) +unsigned Mothership::OnSuper(PMsg_p * Z, unsigned cIdx) // Main hook for user-defined supervisor commands { // set up a return message if we are going to send some reply back @@ -814,7 +916,7 @@ return 0; //------------------------------------------------------------------------------ -unsigned TMoth::OnSyst(PMsg_p * Z, unsigned cIdx) +unsigned Mothership::OnSyst(PMsg_p * Z, unsigned cIdx) // Handler for system commands sent by the user or operator { unsigned key = Z->Key(); @@ -848,7 +950,7 @@ return 0; //------------------------------------------------------------------------------ -unsigned TMoth::OnTinsel(PMsg_p * Z, unsigned cIdx) +unsigned Mothership::OnTinsel(PMsg_p * Z, unsigned cIdx) // Handler for direct packets to be injected into the network from an external source { vector msgs; // messages are packed in Tinsel message format @@ -866,7 +968,7 @@ return 0; //------------------------------------------------------------------------------ -unsigned TMoth::OnTinselOut(P_Sup_Msg_t * packet) +unsigned Mothership::OnTinselOut(P_Sup_Msg_t * packet) // Deals with what happens when a Tinsel message is received. Generally we // repack the message for delivery to the Supervisor handler and deal with // it there. The Supervisor can do one of 2 things: A) process it itself, @@ -902,7 +1004,48 @@ return OnSuper(&W, 0); //------------------------------------------------------------------------------ -void TMoth::StopTwig() +// Mothership sends. These forward packed groups of messages into the Tinsel +// network. Stubbed for the moment until we have external devices +// to test the send into a Mothership. + +unsigned Mothership::SendAttr(PMsg_p *msg, unsigned comm) +{ + return SBase::SendAttr(msg, comm); +} + +unsigned Mothership::SendDevIAll(PMsg_p *msg, unsigned comm) +{ + return SBase::SendDevIAll(msg, comm); +} + +unsigned Mothership::SendDevIByName(PMsg_p *msg, unsigned comm) +{ + return SBase::SendDevIByName(msg, comm); +} + +unsigned Mothership::SendDevIByID(PMsg_p *msg, unsigned comm) +{ + return SBase::SendDevIByID(msg, comm); +} + +unsigned Mothership::SendDevT(PMsg_p *msg, unsigned comm) +{ + return SBase::SendDevT(msg, comm); +} + +unsigned Mothership::SendExtn(PMsg_p *msg, unsigned comm) +{ + return SBase::SendExtn(msg, comm); +} + +unsigned Mothership::SendSupv(PMsg_p *msg, unsigned comm) +{ + return SBase::SendSupv(msg, comm); +} + +//------------------------------------------------------------------------------ + +void Mothership::StopTwig() // Cleanly shut down (or try to) the Twig process. This occurs whenever we // end a task in preparation for shutting down, or exiting. { @@ -925,7 +1068,7 @@ twig_running = false; //------------------------------------------------------------------------------ -unsigned TMoth::SystHW(const vector& args) +unsigned Mothership::SystHW(const vector& args) // Execute some command directly on the Mothership itself. Unlike a Supervisor // message, which triggers the Supervisor function but doesn't change the // functionality of the Mothership, this can directly interact with the @@ -939,7 +1082,7 @@ return system(cmd.str().c_str()); //------------------------------------------------------------------------------ -unsigned TMoth::SystKill() +unsigned Mothership::SystKill() // Kill some process in the local MPI universe. We had better be sure there is // no traffic destined for this process after such a brutal command! { @@ -948,7 +1091,7 @@ return 1; //------------------------------------------------------------------------------ -unsigned TMoth::SystShow() +unsigned Mothership::SystShow() // Report the list of processes (active Motherships) on this comm { return 0; @@ -956,9 +1099,118 @@ return 0; //------------------------------------------------------------------------------ -unsigned TMoth::SystTopo() +unsigned Mothership::SystTopo() // Report this Mothership's local topology information (boards/cores/connections) { return 0; } + +//------------------------------------------------------------------------------ + +void* Mothership::Twig(void* par) +// the Twig processing deals with receiving packets from the managed Tinsel cores. +// Ideally, Twig, OnIdle, and OnTinselOut would reside in a separate *process*. +// For the moment we run Twig in a separate *thread* because to run a separate +// process would require identifying the matching Branch's rank, which could be +// done, using a variety of methods, but none of them trivial, and so that's +// 'for later'. +{ + Mothership* parent = static_cast(par); + const uint32_t szFlit = (1<(recv_buf); + FILE* OutFile; + char Line[4*P_MSG_MAX_SIZE]; + fpos_t readPos; + fpos_t writePos; + if ( (OutFile = fopen("./DebugOutput.txt", "a+")) ) + { + fsetpos(OutFile, &readPos); + fsetpos(OutFile, &writePos); + } + while (parent->ForwardMsgs) // until told otherwise, + { + // receive all available traffic. Should this be done or only one packet + // and then try again for MPI? We don't expect MPI traffic to be intensive + // and tinsel messsages might be time-critical. + while (parent->canRecv()) + { + DebugPrint("Message received from a Device\n"); + parent->recv(recv_buf); + uint32_t* device = static_cast(p_recv_buf); // get the first word, which will be a device address + if (!(*device & P_SUP_MASK)) // bound for an external? + { + P_Msg_Hdr_t* m_hdr = static_cast(p_recv_buf); + DebugPrint("Message is bound for external device %d\n", m_hdr->destDeviceAddr); + if (parent->TwigExtMap[m_hdr->destDeviceAddr] == 0) + parent->TwigExtMap[m_hdr->destDeviceAddr] = new deque; + if (m_hdr->messageLenBytes > szFlit) + parent->recvMsg(recv_buf+szFlit, m_hdr->messageLenBytes-szFlit); + parent->TwigExtMap[m_hdr->destDeviceAddr]->push_back(*(static_cast(p_recv_buf))); + } + else + { + P_Sup_Hdr_t* s_hdr = static_cast(p_recv_buf); + s_hdr->sourceDeviceAddr ^= P_SUP_MASK; + + if (s_hdr->command == P_PKT_MSGTYP_ALIVE) + { + DebugPrint("Thread %d is still alive\n", s_hdr->sourceDeviceAddr >> P_THREAD_OS); + } + else + { + DebugPrint("Message is a Supervisor request from device %d\n", s_hdr->sourceDeviceAddr); + if (parent->TwigMap[s_hdr->sourceDeviceAddr] == 0) // new device talking? + { + DebugPrint("New device %d reporting to Supervisor\n", s_hdr->sourceDeviceAddr); + parent->TwigMap[s_hdr->sourceDeviceAddr] = new PinBuf_t; + } + if ((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] == 0) // inactive pin for the device? + { + DebugPrint("New pin %d for device %d reporting to Supervisor\n", s_hdr->destPin, s_hdr->sourceDeviceAddr); + (*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] = new char[MAX_P_SUP_MSG_BYTES](); + } + P_Sup_Msg_t* recvdMsg = static_cast(static_cast((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin])); + memcpy(recvdMsg+s_hdr->seq,s_hdr,p_sup_hdr_size()); // stuff header into the persistent buffer + DebugPrint("Expecting message of total length %d\n", s_hdr->cmdLenBytes); + uint32_t len = s_hdr->seq == s_hdr->cmdLenBytes/p_sup_msg_size() ? s_hdr->cmdLenBytes%p_sup_msg_size() : p_sup_msg_size(); // more message to receive? + DebugPrint("Length for sequence number %d: %d\n", s_hdr->seq, len); + if (len > szFlit) parent->recvMsg(((recvdMsg+s_hdr->seq)->data), len-szFlit); // get the whole message + if (super_buf_recvd(recvdMsg)) + { + DebugPrint("Entire Supervisor message received of length %d\n", s_hdr->cmdLenBytes); + if (parent->OnTinselOut(recvdMsg)) + parent->Post(530, int2str(parent->Urank)); + super_buf_clr(recvdMsg); + } + } + } + } + // Capture anything happening on the DebugLink - which is text output directed at a file. + bool updated = false; + if (OutFile) + { + fsetpos(OutFile, &writePos); + while (parent->pollStdOut(OutFile)) updated = true; + if (updated) + { + DebugPrint("Received a debug output message\n"); + } + fgetpos(OutFile, &writePos); + } + else while (parent->pollStdOut()); // or possibly only dumped to the local console + // output the debug output buffer, which has to be done immediately because we are in a separate thread + if (OutFile && updated) + { + fflush(OutFile); + fsetpos(OutFile, &readPos); + while (!feof(OutFile)) parent->Post(600, string(fgets(Line, 4*P_MSG_MAX_SIZE, OutFile))); + fgetpos(OutFile, &readPos); + } + } + printf("Exiting Twig thread\n"); + pthread_exit(par); + return par; +} + //============================================================================== diff --git a/Source/Mothership/Mothership.h b/Source/Mothership/Mothership.h new file mode 100644 index 00000000..dda195dd --- /dev/null +++ b/Source/Mothership/Mothership.h @@ -0,0 +1,107 @@ +#ifndef __MOTHERSHIP__H +#define __MOTHERSHIP__H + +#include +#include "SBase.h" +#include "Debug.h" +#include "PMsg_p.hpp" +#include "Cli.h" +#include "HostLink.h" +#include "pthread.h" +#include "TaskInfo.h" +#include "poets_msg.h" +#include "P_addr.h" + +//============================================================================== + +class Mothership : public SBase, public HostLink +{ + +public: + Mothership(int,char **,string); +virtual ~ Mothership(); + +unsigned Connect(string=""); + +// somewhat bodgey function to return the hardware address from the composite; needed because of +// the actual hardware mappings; this is the Orchestrator-side equivalent of toAddr. +static inline unsigned GetHWAddr(P_addr& VAddr) {return (VAddr.A_box << P_BOX_HWOS) | + (VAddr.A_board << P_BOARD_HWOS) | + (VAddr.A_mailbox << P_MAILBOX_HWOS) | + (VAddr.A_core << P_CORE_HWOS) | + (VAddr.A_thread << P_THREAD_HWOS);}; +// static void* LoadBoard(void*); // threaded version of bootloader +static void* Twig (void*); // thread to handle Tinsel messages + +private: +unsigned Boot (string); +unsigned CmLoad (string); +unsigned CmRun (string); +unsigned CmStop (string); +unsigned ConfigDir (PMsg_p *, unsigned); +unsigned ConfigDistribute(PMsg_p *, unsigned); +unsigned ConfigRecall (PMsg_p *, unsigned); +unsigned ConfigState (PMsg_p *, unsigned); +#include "SDecode.cpp" +inline virtual string Dname(){ return typeid(*this).name(); } +void Dump (FILE * = stdout, string = ""); +unsigned DumpAll (PMsg_p *, unsigned); +unsigned DumpSummary (PMsg_p *, unsigned); +unsigned DumpTask (PMsg_p *, unsigned); +long LoadBoard (P_board*); // unthreaded version of bootloader +unsigned OnCfg (PMsg_p *,unsigned); +unsigned OnCmnd (PMsg_p *,unsigned); +unsigned OnDump (PMsg_p *,unsigned); +unsigned OnExit (PMsg_p *,unsigned); +void OnIdle(); +unsigned OnSend (PMsg_p *, unsigned); +unsigned OnSuper (PMsg_p *,unsigned); +unsigned OnSyst (PMsg_p *,unsigned); +unsigned OnTinsel (PMsg_p*, unsigned); +unsigned OnTinselOut (P_Sup_Msg_t *); +void StopTwig(); +// forwarded messages containing POETS packets. The Mothership +// should unpack the packets contained and inject them using +// the HostLink. (This could be placed in a Branch thread). +unsigned SendAttr (PMsg_p *, unsigned); +unsigned SendDevIAll (PMsg_p *, unsigned); +unsigned SendDevIByName (PMsg_p *, unsigned); +unsigned SendDevIByID (PMsg_p *, unsigned); +unsigned SendDevT (PMsg_p *, unsigned); +unsigned SendExtn (PMsg_p *, unsigned); +unsigned SendSupv (PMsg_p *, unsigned); +unsigned SystHW (const vector&); +unsigned SystKill(); +unsigned SystShow(); +unsigned SystTopo(); + +void* SuperHandle; // dynamically loadable supervisor +int (*SupervisorCall)(PMsg_p*, PMsg_p*); // entry point for the Supervisor + +public: +unsigned PAddress; // address of this mothership in POETS-space +bool ForwardMsgs; + +typedef unsigned (Mothership::*pMeth)(PMsg_p *,unsigned); +typedef map FnMap_t; +typedef map PinBuf_t; // type to hold buffers for messages received from devices + +map*> TwigExtMap; // dynamic queues for messages bound for external devices +map TwigMap; // dynamic buffer state for each device from which the Mothership is receiving +map TaskMap; // which tasks are mapped to the machine +vector> BootMap; // which booter is starting which board +vector FnMapx; +pthread_t Twig_thread; // thread for processing tinsel connections +bool twig_running; // flag to show twig thread is active + +static const int NumBoards = NUM_BOARDS_PER_BOX; + +}; + +//============================================================================== + +#endif + + + + diff --git a/Source/Mothership/MothershipMain.cpp b/Source/Mothership/MothershipMain.cpp index 3a417a00..e86c4926 100644 --- a/Source/Mothership/MothershipMain.cpp +++ b/Source/Mothership/MothershipMain.cpp @@ -1,10 +1,11 @@ -#include "TMoth.h" +#include "Mothership.h" +//#include "TMoth.h" #include "Pglobals.h" #include int main(int argc, char * argv[]) { - TMoth* mothership = new TMoth(argc, argv, string(csMOTHERSHIPproc)); + Mothership* mothership = new Mothership(argc, argv, string(csMOTHERSHIPproc)); delete mothership; printf("%s Main closing down\n", csMOTHERSHIPproc); fflush(stdout); diff --git a/Source/Mothership/TMoth.h b/Source/Mothership/TMoth.h deleted file mode 100644 index 729d8ce7..00000000 --- a/Source/Mothership/TMoth.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef __TMothH__H -#define __TMothH__H - -#include -#include "CommonBase.h" -#include "Debug.h" -#include "PMsg_p.hpp" -#include "Cli.h" -#include "HostLink.h" -#include "pthread.h" -#include "TaskInfo.h" -#include "poets_msg.h" -#include "P_addr.h" - -//============================================================================== - -class TMoth : public CommonBase, public HostLink -{ - -public: - TMoth(int,char **,string); -virtual ~ TMoth(); - -// somewhat bodgey function to return the hardware address from the composite; needed because of -// the actual hardware mappings; this is the Orchestrator-side equivalent of toAddr. -static inline unsigned GetHWAddr(P_addr& VAddr) {return (VAddr.A_box << P_BOX_HWOS) | (VAddr.A_board << P_BOARD_HWOS) | (VAddr.A_core << P_CORE_HWOS) | (VAddr.A_thread << P_THREAD_HWOS);}; -// static void* LoadBoard(void*); // threaded version of bootloader -static void* Twig(void*); // thread to handle Tinsel messages - -private: -unsigned Boot(string); -unsigned CmLoad(string); -unsigned CmRun(string); -unsigned CmStop(string); -#include "Decode.cpp" -inline virtual string Dname(){ return typeid(*this).name(); } -void Dump(FILE * = stdout); -long LoadBoard(P_board*); // unthreaded version of bootloader -unsigned NameDist(PMsg_p*); -unsigned NameRecl(PMsg_p*); -unsigned NameTdir(const string&, const string&); -unsigned OnCmnd(PMsg_p *,unsigned); -unsigned OnExit(PMsg_p *,unsigned); -void OnIdle(); -unsigned OnName(PMsg_p *,unsigned); -unsigned OnSuper(PMsg_p *,unsigned); -unsigned OnSyst(PMsg_p *,unsigned); -unsigned OnTinsel(PMsg_p*, unsigned); -unsigned OnTinselOut(P_Sup_Msg_t *); -void StopTwig(); -unsigned SystHW(const vector&); -unsigned SystKill(); -unsigned SystShow(); -unsigned SystTopo(); - -void* SuperHandle; // dynamically loadable supervisor -int (*SupervisorCall)(PMsg_p*, PMsg_p*); // entry point for the Supervisor - -public: -unsigned PAddress; // address of this mothership in POETS-space -bool ForwardMsgs; - -typedef unsigned (TMoth::*pMeth)(PMsg_p *,unsigned); -typedef map FnMap_t; -typedef map PinBuf_t; // type to hold buffers for messages received from devices - -map*> TwigExtMap; // dynamic queues for messages bound for external devices -map TwigMap; // dynamic buffer state for each device from which the Mothership is receiving -map TaskMap; // which tasks are mapped to the machine -vector> BootMap; // which booter is starting which board -vector FnMapx; -pthread_t Twig_thread; // thread for processing tinsel connections -bool twig_running; // flag to show twig thread is active - -static const int NumBoards = NUM_BOARDS_PER_BOX; - -}; - -//============================================================================== - -#endif - - - - diff --git a/Source/NameServer/AddressBook.cpp b/Source/NameServer/AddressBook.cpp index 1f273314..61eb4368 100644 --- a/Source/NameServer/AddressBook.cpp +++ b/Source/NameServer/AddressBook.cpp @@ -1,5 +1,6 @@ //============================================================================== #include "AddressBook.hpp" +//#include "Debug.h" #include @@ -513,6 +514,7 @@ unsigned AddressBook::AddDevice(std::string &TaskName, Record_t &DevRec, bool Va TaskRecord_t *TRec = FindTask(TaskName); if(TRec == PNULL) { + // DebugPrint("No such task: %s\n", TaskName.c_str()); ERETURN("Task Does Not Exist", ERR_TASK_NOT_FOUND); } @@ -521,12 +523,14 @@ unsigned AddressBook::AddDevice(std::string &TaskName, Record_t &DevRec, bool Va if (Validate) // Check that the device does not already exist { // and that the indices are valid. unsigned Ret = ValidateDevice(TRec, DevRec); + // DebugPrint("Device validation %s\n", (Ret ? "failed" : "succeeded")); if(Ret) return Ret; } if ((DevRec.RecordType == Device) || (DevRec.RecordType == DeviceExt)) { // Add a Device + // DebugPrint("Ordinary device being added\n"); CheckVector(TRec, TRec->Devices); // Sanity check for reallocation. TRec->Devices.push_back(DevRec); // Add Dev to task. @@ -538,6 +542,7 @@ unsigned AddressBook::AddDevice(std::string &TaskName, Record_t &DevRec, bool Va } else if (DevRec.RecordType == External) { // Add an External + // DebugPrint("External device being added\n"); CheckVector(TRec, TRec->Externals); // Sanity check for reallocation TRec->Externals.push_back(DevRec); @@ -549,6 +554,7 @@ unsigned AddressBook::AddDevice(std::string &TaskName, Record_t &DevRec, bool Va } else if (DevRec.RecordType == Supervisor) { // Add a Supervisor + // DebugPrint("Supervisor device being added\n"); CheckVector(TRec, TRec->Supervisors); // Sanity check for reallocation. TRec->Supervisors.push_back(DevRec); @@ -570,8 +576,8 @@ unsigned AddressBook::AddDevice(std::string &TaskName, Record_t &DevRec, bool Va // added 18 July 2019 ADR - if Map and Link were previously invalidated // rebuild everything unsigned err; - if (!TRec->MapValid && ((err = BuildMaps(TRec->Name)) != SUCCESS)) return err; - if (!TRec->LinkValid && ((err = BuildLink(TRec->Name)) != SUCCESS)) return err; + if (!TRec->MapValid && ((err = BuildMaps(TRec->Name)) != SUCCESS)) return err; + if (!TRec->LinkValid && ((err = BuildLink(TRec->Name)) != SUCCESS)) return err; return SUCCESS; } @@ -1023,20 +1029,24 @@ unsigned AddressBook::ValidateDevice(TaskRecord_t *TRec, Record_t &DevRec) // Check that the device does not already exist in the maps AddrMap_t::const_iterator DASearch = TRec->AddrMap.find(DevRec.Address); if (DASearch != TRec->AddrMap.end()) { + // DebugPrint("Device address %llu already exists\n", DevRec.Address); ERETURN("DUPADDR: Device Address already added", ERR_DEVICE_ADDR_USED); } NameMap_t::const_iterator DNSearch = TRec->NameMap.find(DevRec.Name); if (DNSearch != TRec->NameMap.end()) { + // DebugPrint("Device name %s already exists\n", DevRec.Name.c_str()); ERETURN("DUPNAME: Device Name already added", ERR_DEVICENAME_USED); } // Check that the indices are valid BEFORE adding the Device. if (DevRec.DeviceType >= TRec->DevTypes.size()) { + // DebugPrint("Device type %u does not exist\n", DevRec.DeviceType); ERETURN("Invalid DeviceType Index", ERR_INVALID_DEVTYPE); } if (DevRec.Attribute >= static_cast(TRec->AttrTypes.size())) { + // DebugPrint("Device attribute type %u does not exist\n", DevRec.Attribute); ERETURN("Invalid AttributeType Index", ERR_INVALID_ATTRIBUTE); } diff --git a/Source/NameServer/NameServer.cpp b/Source/NameServer/NameServer.cpp index ef6c012d..50b77c60 100644 --- a/Source/NameServer/NameServer.cpp +++ b/Source/NameServer/NameServer.cpp @@ -203,6 +203,7 @@ unsigned NameServer::ConfigDistribute(PMsg_p * msg, unsigned comm) // if ((s->Address == *superAddr) && (*m == static_cast(s->Rank))) // SymAddr_t form if ((static_cast(s->Rank) == *superAddr) && (*m == static_cast(s->Rank))) { + DebugPrint("Deploying name data to Mothership at rank %d\n",*m); // send over the task NameServer data PMsg_p taskInfo(Comms[sComm]); taskInfo.Src(Urank); diff --git a/Source/NameServer/NameServer.h b/Source/NameServer/NameServer.h index 31a8b9da..aa877438 100644 --- a/Source/NameServer/NameServer.h +++ b/Source/NameServer/NameServer.h @@ -2,6 +2,7 @@ #define __NameServerH__H #include "SBase.h" +#include "Debug.h" // #include "Ns_el.h" //============================================================================== diff --git a/Source/NameServer/SBase.cpp b/Source/NameServer/SBase.cpp index e9b374c1..b9af493d 100644 --- a/Source/NameServer/SBase.cpp +++ b/Source/NameServer/SBase.cpp @@ -4,6 +4,22 @@ #include // debug +const map SBase::StateVals({{"Loaded",Loaded}, + {"Linked",Linked}, + {"Built",Built}, + {"Deployed",Deployed}, + {"Init",Init}, + {"Running",Running}, + {"Finished",Finished}, + {"Unknown",Unknown}}); +const map SBase::StateStrs({{Loaded,"Loaded"}, + {Linked,"Linked"}, + {Built,"Built"}, + {Deployed,"Deployed"}, + {Init,"Init"}, + {Running,"Running"}, + {Finished,"Finished"}, + {Unknown,"Unknown"}}); // constructor. Arguments will almost always come from higher-level // constructors since we expect to derive from SBase. SBase::SBase(int argc,char ** argv,string d,string s): CommonBase(argc,argv,d,s), AddressBook(d) @@ -1400,7 +1416,7 @@ unsigned SBase::QueryTask(PMsg_p *msg, unsigned comm) if (taskName.empty()) // and no task name is an error. { Post(710, "QueryTask", int2str(Urank)); - return AddressBook::ERR_INVALID_TASK; + return AddressBook::ERR_NONFATAL; } unsigned err = SUCCESS; int srcProc = msg->Src(); // set up the reply @@ -1516,24 +1532,9 @@ unsigned SBase::SendSupv(PMsg_p *msg, unsigned comm) //============================================================================== string SBase::State2Str(TaskState_t state) { - switch (state) - { - case Loaded: - return "Loaded"; - case Linked: - return "Linked"; - case Built: - return "Built"; - case Deployed: - return "Deployed"; - case Init: - return "Init"; - case Running: - return "Running"; - case Finished: - return "Finished"; - } - return "Unknown"; + map::const_iterator s = StateStrs.find(state); + if (s == StateStrs.end()) return "Unknown"; + else return s->second; } //============================================================================== @@ -1543,13 +1544,7 @@ string SBase::State2Str(TaskState_t state) //============================================================================== TaskState_t SBase::Str2State(const string& state) { - - if (state == "Loaded") return Loaded; - if (state == "Linked") return Linked; - if (state == "Built") return Built; - if (state == "Deployed") return Deployed; - if (state == "Init") return Init; - if (state == "Running") return Running; - if (state == "Finished") return Finished; - return Unknown; + map::const_iterator s = StateVals.find(state); + if (s == StateVals.end()) return Unknown; + else return s->second; } diff --git a/Source/NameServer/SBase.h b/Source/NameServer/SBase.h index 843898a9..cadd884b 100644 --- a/Source/NameServer/SBase.h +++ b/Source/NameServer/SBase.h @@ -99,6 +99,9 @@ unsigned QueryExtn (PMsg_p *, unsigned); unsigned QueryList (PMsg_p *, unsigned); unsigned QueryTask (PMsg_p *, unsigned); +static const map StateVals; +static const map StateStrs; + }; //============================================================================== diff --git a/Source/OrchBase/CMsg_p.cpp b/Source/OrchBase/CMsg_p.cpp index 04d51023..f87cf402 100644 --- a/Source/OrchBase/CMsg_p.cpp +++ b/Source/OrchBase/CMsg_p.cpp @@ -65,8 +65,14 @@ void CMsg_p::Get(vector > & vP) unsigned* pCore = PMsg_p::Get(5,len[4]); unsigned* pThread = PMsg_p::Get(6,len[5]); unsigned* pDevice = PMsg_p::Get(7,len[6]); - if ((len[6] | len[5] | len[4] | len[3] | len[2] | len[1]) != len[0]); - if (!pCIdx || !pBox || !pBoard || !pMailbox || !pCore || !pThread || !pDevice); + if ((len[6] | len[5] | len[4] | len[3] | len[2] | len[1]) != len[0]) + { + // what is the point of this empty if statement? + } + if (!pCIdx || !pBox || !pBoard || !pMailbox || !pCore || !pThread || !pDevice) + { + // what is the point of this empty if statement? + } for (int core = 0; core < len[0]; core++) vP.push_back(pair(pCIdx[core],P_addr_t(pBox[core],pBoard[core],pMailbox[core],pCore[core],pThread[core],pDevice[core]))); } diff --git a/Source/OrchBase/Constraints.h b/Source/OrchBase/Constraints.h index 2041b28b..406a063c 100644 --- a/Source/OrchBase/Constraints.h +++ b/Source/OrchBase/Constraints.h @@ -3,6 +3,7 @@ #include #include "NameBase.h" +#include "OSFixes.hpp" //============================================================================== diff --git a/Source/OrchBase/HardwareConfigurationDeployment/Dialect1Deployer.cpp b/Source/OrchBase/HardwareConfigurationDeployment/Dialect1Deployer.cpp index 147c060c..c5b405df 100644 --- a/Source/OrchBase/HardwareConfigurationDeployment/Dialect1Deployer.cpp +++ b/Source/OrchBase/HardwareConfigurationDeployment/Dialect1Deployer.cpp @@ -94,7 +94,7 @@ void Dialect1Deployer::name_engine(P_engine* engine) { if ((engine->Name().size() == 0 || engine->Name() == "**undefined**") && - fileOrigin.size() >= 0) + fileOrigin.size() > 0) { engine->Name(fileOrigin); } diff --git a/Source/OrchBase/HardwareFileManager/HardwareFileNotFoundException.h b/Source/OrchBase/HardwareFileManager/HardwareFileNotFoundException.h deleted file mode 100644 index 7d042543..00000000 --- a/Source/OrchBase/HardwareFileManager/HardwareFileNotFoundException.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEMANAGER_HARDWAREFILENOTFOUNDEXCEPTION_H -#define __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEMANAGER_HARDWAREFILENOTFOUNDEXCEPTION_H - -/* Describes an exception that is to be thrown when a path is passed to the - * hardware file parser, which does not correspond to a file. */ - -#include "HardwareFileParserException.h" - -class HardwareFileNotFoundException: public HardwareFileParserException -{ -public: - HardwareFileNotFoundException(std::string message): - HardwareFileParserException(message){}; -}; - -#endif diff --git a/Source/OrchBase/HardwareFileManager/HardwareFileNotLoadedException.h b/Source/OrchBase/HardwareFileManager/HardwareFileNotLoadedException.h deleted file mode 100644 index ecde23a5..00000000 --- a/Source/OrchBase/HardwareFileManager/HardwareFileNotLoadedException.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEMANAGER_HARDWAREFILENOTLOADEDEXCEPTION_H -#define __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEMANAGER_HARDWAREFILENOTLOADEDEXCEPTION_H - -/* Describes an exception that is to be thrown when the hardware file parser is - * ordered to populate a deployer before it has loaded a file. */ - -#include "HardwareFileParserException.h" - -class HardwareFileNotLoadedException: public HardwareFileParserException -{ -public: - HardwareFileNotLoadedException():HardwareFileParserException{""} - {message = "[ERROR] Engine-population attempted without first " - "loading a file.\n";} -}; - -#endif diff --git a/Source/OrchBase/HardwareFileManager/HardwareFileParser.h b/Source/OrchBase/HardwareFileManager/HardwareFileParser.h deleted file mode 100644 index 27ddac1f..00000000 --- a/Source/OrchBase/HardwareFileManager/HardwareFileParser.h +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEMANAGER_HARDWAREFILEPARSER_H -#define __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEMANAGER_HARDWAREFILEPARSER_H - -/* Logic for parsing hardware model files, and for generating hardware models - * from them. Example: - * - * P_engine* engine = new P_engine("My engine"); - * parser = HardwareFileParser("/path/to/my/hardware/file.uif", engine); - * // We're done here. - * - * Or, for more points: - * - * P_engine* engine = new P_engine("My engine"); - * parser = HardwareFileParser; - * try - * { - * parser.load_file("/path/to/my/hardware/file.uif"); - * parser.populate_hardware_model(engine); - * } - * catch (OrchestratorException& exception) - * { - * printf("%s\n", exception.what()) - * } - * // We're done here. - * - * Hardware model files are UIF files that fully define a model of the - * hardware, which is suitable for the Orchestrator's purposes. - * - * Roughly speaking, the conversion from a hardware model file to a hardware - * model follows this procedure: - * - * 1. Load the file. - * 2. Parse the file using the UIF parser, and generate a token tree. Raises - * if the file is syntactically invalid. - * 3. Parse the token tree, and generate a hardware stack (PoetsEngine - * populated with components). Raises if the file is semantically invalid. - * ... - * From which the programmer can use the hardware model as they desire (for - * the most part, this means putting it into the Orchestrator, but do what you - * will). - * - * The HardwareFileParser class encapsulates this process (I wouldn't normally - * do it this way, but the recommended approach for using UIF is to use it as a - * base class, so...). Only one file can be loaded by this parser at a time; - * loading a second file clobbers the data structure of the first. - * - * As part of this process, the input file is validated. Points that are not - * currently validated include: - * - * - Lines within a section that are simply one word, without the assignment - * operator (=) or the prefix (+). These are simply ignored. - * - * - Multidimensional input components are not matched against their respective - * address word lengths, either using their dimensionality or their value. - * - * - Repeatedly-defined values within a section - only the last defined value - * is used. - * - * The contents of the engine are only replaced if validation succeeds. This - * replacement requires the engine to be dynamically allocated. - * - * See the hardware model documentation for a description of the file - * format. */ - -#include -#include -#include - -#include "Dialect1Deployer.h" -#include "HardwareAddressFormat.h" -#include "HardwareAddress.h" -#include "HardwareFileNotFoundException.h" -#include "HardwareFileNotLoadedException.h" -#include "HardwareModel.h" -#include "HardwareSemanticException.h" -#include "HardwareSyntaxException.h" - -#include "dfprintf.h" -#include "flat.h" -#include "jnj.h" /* JNJ is a wrapper around UIF that provides nicer access to - * the UIF data structure. */ - -class HardwareFileParser: public JNJ -{ -public: - HardwareFileParser(); - HardwareFileParser(const char* filePath, P_engine* engine); - void load_file(const char* filePath); - void populate_hardware_model(P_engine* engine); - -private: - bool isFileLoaded; - std::string loadedFile; - bool does_file_exist(const char* filePath); - void set_uif_error_callback(); - static void on_syntax_error(void*, void*, int); - - /* Validation methods. */ - bool validate_section_contents(std::string* errorMessage); - bool validate_sections(std::string* errorMessage); - bool provision_deployer(Dialect1Deployer* deployer, - std::string* errorMessage); - - /* Node-checking methods. */ - bool is_value_at_node_natural(UIF::Node* recordNode, UIF::Node* valueNode, - std::string variable, std::string value, - std::string sectionName, - std::string* errorMessage); - bool is_value_at_node_floating(UIF::Node* recordNode, UIF::Node* valueNode, - std::string variable, std::string value, - std::string sectionName, - std::string* errorMessage); - bool are_values_at_node_natural(UIF::Node* recordNode, - UIF::Node* valueNode, std::string variable, - std::string value, std::string sectionName, - std::string* errorMessage); - void invalid_variable_message(std::string* errorMessage, - UIF::Node* recordNode, - std::string sectionName, - std::string variable); -}; - -float str2float(std::string floatLike); -unsigned str2unsigned(std::string unsignedLike); - -#endif diff --git a/Source/OrchBase/HardwareFileManager/HardwareFileParserException.h b/Source/OrchBase/HardwareFileManager/HardwareFileParserException.h deleted file mode 100644 index 97bf4c1a..00000000 --- a/Source/OrchBase/HardwareFileManager/HardwareFileParserException.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEMANAGER_HARDWAREFILEPARSEREXCEPTION_H -#define __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEMANAGER_HARDWAREFILEPARSEREXCEPTION_H - -/* Describes an exception that is to be thrown from the parser (intended as a - * base class). */ - -#include "OrchestratorException.h" -class HardwareFileParserException: public OrchestratorException -{ -public: - HardwareFileParserException(std::string message): - OrchestratorException(message){}; -}; - -#endif diff --git a/Source/OrchBase/HardwareFileManager/HardwareSemanticException.h b/Source/OrchBase/HardwareFileManager/HardwareSemanticException.h deleted file mode 100644 index 6fd79f8f..00000000 --- a/Source/OrchBase/HardwareFileManager/HardwareSemanticException.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEMANAGER_HARDWARESEMANTICEXCEPTION_H -#define __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEMANAGER_HARDWARESEMANTICEXCEPTION_H - -/* Describes an exception that is to be thrown when a hardware file is passed - * to the hardware file parser, and when the file contains a semantic error. */ - -#include "HardwareFileParserException.h" - -class HardwareSemanticException: public HardwareFileParserException -{ -public: - HardwareSemanticException(std::string message): - HardwareFileParserException(message){}; -}; - -#endif diff --git a/Source/OrchBase/HardwareFileManager/HardwareSyntaxException.h b/Source/OrchBase/HardwareFileManager/HardwareSyntaxException.h deleted file mode 100644 index 0f34c1d1..00000000 --- a/Source/OrchBase/HardwareFileManager/HardwareSyntaxException.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEMANAGER_HARDWARESYNTAXEXCEPTION_H -#define __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEMANAGER_HARDWARESYNTAXEXCEPTION_H - -/* Describes an exception that is to be thrown when a hardware file is passed - * to the hardware file parser, and when the file contains a syntax error. */ - -#include "HardwareFileParserException.h" - -class HardwareSyntaxException: public HardwareFileParserException -{ -public: - HardwareSyntaxException(std::string message): - HardwareFileParserException(message){}; -}; - -#endif diff --git a/Source/OrchBase/HardwareFileReader/HardwareFileNotFoundException.h b/Source/OrchBase/HardwareFileReader/HardwareFileNotFoundException.h new file mode 100644 index 00000000..908e3d72 --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/HardwareFileNotFoundException.h @@ -0,0 +1,16 @@ +#ifndef __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_HARDWAREFILENOTFOUNDEXCEPTION_H +#define __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_HARDWAREFILENOTFOUNDEXCEPTION_H + +/* Describes an exception that is to be thrown when a path is passed to the + * hardware file reader, which does not correspond to a file. */ + +#include "HardwareFileReaderException.h" + +class HardwareFileNotFoundException: public HardwareFileReaderException +{ +public: + HardwareFileNotFoundException(std::string message): + HardwareFileReaderException(message){}; +}; + +#endif diff --git a/Source/OrchBase/HardwareFileReader/HardwareFileNotLoadedException.h b/Source/OrchBase/HardwareFileReader/HardwareFileNotLoadedException.h new file mode 100644 index 00000000..defd46c8 --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/HardwareFileNotLoadedException.h @@ -0,0 +1,17 @@ +#ifndef __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_HARDWAREFILENOTLOADEDEXCEPTION_H +#define __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_HARDWAREFILENOTLOADEDEXCEPTION_H + +/* Describes an exception that is to be thrown when the hardware file reader is + * ordered to populate a deployer before it has loaded a file. */ + +#include "HardwareFileReaderException.h" + +class HardwareFileNotLoadedException: public HardwareFileReaderException +{ +public: + HardwareFileNotLoadedException():HardwareFileReaderException{""} + {message = "[ERROR] Engine-population attempted without first " + "loading a file.\n";} +}; + +#endif diff --git a/Source/OrchBase/HardwareFileReader/HardwareFileReader.h b/Source/OrchBase/HardwareFileReader/HardwareFileReader.h new file mode 100644 index 00000000..c50bb01c --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/HardwareFileReader.h @@ -0,0 +1,256 @@ +#ifndef __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_HARDWAREFILEREADER_H +#define __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_HARDWAREFILEREADER_H + +/* Logic for parsing hardware model files, and for generating hardware models + * from them. Example: + * + * P_engine* engine = new P_engine("My engine"); + * reader = HardwareFileReader("/path/to/my/hardware/file.uif", engine); + * // We're done here. + * + * Or, for more points: + * + * P_engine* engine = new P_engine("My engine"); + * reader = HardwareFileReader; + * try + * { + * reader.load_file("/path/to/my/hardware/file.uif"); + * reader.populate_hardware_model(engine); + * } + * catch (OrchestratorException& exception) + * { + * printf("%s\n", exception.what()) + * } + * // We're done here. + * + * Hardware model files are UIF files that fully define a model of the + * hardware, which is suitable for the Orchestrator's purposes. + * + * Roughly speaking, the conversion from a hardware model file to a hardware + * model follows this procedure: + * + * 1. Load the file. + * 2. Parse the file using the UIF reader, and generate a tree. Raises if the + * file is syntactically invalid. + * 3. Traverse the generated tree, and generate a hardware stack (P_engine + * populated with components). Raises if the file is semantically invalid. + * + * From which the programmer can use the hardware model as they desire (for + * the most part, this means putting it into the Orchestrator, but do what you + * will). + * + * The HardwareFileReader class encapsulates this process (I wouldn't normally + * do it this way, but the recommended approach for using UIF is to use it as a + * base class, so...). Only one file can be loaded by this reader at a time; + * loading a second file clobbers the data structure of the first. + * + * As part of this process, the input file is validated. In dialect 1, things + * that are not currently validated include: + * + * - Lines within a section that are simply one word, without the assignment + * operator (=) or the prefix (+). These are simply ignored. + * + * - Multidimensional input components are not matched against their respective + * address word lengths, either using their dimensionality or their value. + * + * - Repeatedly-defined values within a section - only the last defined value + * is used. + * + * The contents of the engine are only defined if validation succeeds. This + * replacement requires the engine to be dynamically allocated. + * + * The the methods of the class that this header declares are defined in + * multiple files, depending on the dialect they interact with. + * + * See the hardware model documentation for a description of the file + * format. */ + +#include +#include +#include +#include + +#include "Dialect1Deployer.h" +#include "HardwareAddressFormat.h" +#include "HardwareAddress.h" +#include "HardwareFileNotFoundException.h" +#include "HardwareFileNotLoadedException.h" +#include "HardwareModel.h" +#include "HardwareSemanticException.h" +#include "HardwareSyntaxException.h" +#include "Validator.h" + +#include "dfprintf.h" +#include "flat.h" +#include "jnj.h" /* JNJ is a wrapper around UIF that provides nicer access to + * the UIF data structure. */ + +/* Some structs and typedefs used to streamline the structures used by the + * dialect 3 reader for vaidation: + * + * - BoardInfo: Holds information on what line in the input file a board was + * declared, and where the board object is. + * + * - MailboxInfo: Holds information on what line in the input file a mailbox + * was declared, and where the mailbox object is. + * + * - EdgeInfo: Holds information on the determined weight of an edge, whether + * its reverse-direction has been declared (yet), and the line in the input + * file where its instance is first mentioned. + * + * - BoardName: Holds the name of a board. The first string in the pair is the + * name of the box containing the boards. The second string in the pair is + * the name of the board in the box. This is a concession to the fact that + * board names only need to be unique within their box. + * + * - MailboxName: Holds the name of a mailbox. It's relative. + */ +struct BoardInfo{int lineNumber; P_board* memoryAddress;}; +struct MailboxInfo{int lineNumber; P_mailbox* memoryAddress;}; +struct EdgeInfo{float weight; bool isReverseDefined; int lineNumber;}; + +typedef std::pair BoardName; +typedef std::string MailboxName; + +#define ITEM_ENUM_LENGTH 3 +enum item {box, board, mailbox}; + +#define DEFAULT_ENGINE_NAME "UnnamedEngine" + +class HardwareFileReader: public JNJ, public Validator +{ +public: + HardwareFileReader(); + HardwareFileReader(const char* filePath, P_engine* engine); + void load_file(const char* filePath); + void populate_hardware_model(P_engine* engine); + +private: + /* Dialect-independent methods */ + bool isFileLoaded; + std::string loadedFile; + void construct_error_output_string(std::string* target); + bool does_file_exist(const char* filePath); + unsigned get_dialect(); + void get_values_as_strings(std::vector* toPopulate, + UIF::Node* valueNode); + + /* UIF-syntax error methods */ + static void on_syntax_error(void*, void*, int); + void set_uif_error_callback(); + + /* Dialect 1 validation and deployment methods. */ + void d1_invalid_variable_message(); + void d1_populate_hardware_model(P_engine* engine); + bool d1_provision_deployer(Dialect1Deployer* deployer); + bool d1_validate_sections(); + + /* Dialect 3 validation and deployment members and methods. */ + void d3_catastrophic_failure(P_engine* engine); + bool d3_create_cores_and_threads_for_mailbox(P_mailbox* mailbox, + unsigned coreQuantity); + bool d3_define_board_fields_from_section(P_board* board, + UIF::Node* sectionNode); + bool d3_define_box_fields_from_section(P_box* box, UIF::Node* sectionNode); + bool d3_define_mailbox_fields_from_section(P_mailbox* mailbox, + UIF::Node* sectionNode); + bool d3_get_address_from_item_definition(UIF::Node* itemNode, + AddressComponent* address); + bool d3_get_board_name(UIF::Node* itemNode, BoardName* boardName); + bool d3_get_explicit_cost_from_edge_definition(UIF::Node* itemNode, + float* cost); + bool d3_get_explicit_type_from_item_definition(UIF::Node* itemNode, + std::string* type); + bool d3_get_mailbox_name(UIF::Node* itemNode, MailboxName* mailboxName); + bool d3_get_section_from_type(std::string itemType, std::string type, + UIF::Node** sectionNode); + bool d3_get_validate_default_types(UIF::Node** globalDefaults); + bool d3_load_validate_sections(); + void d3_populate_hardware_model(P_engine* engine); + bool d3_populate_validate_address_format(P_engine* engine); + bool d3_populate_validate_board_with_mailboxes(P_board* board, + UIF::Node* sectionNode); + bool d3_populate_validate_engine_board_and_below(P_engine* engine); + bool d3_populate_validate_engine_box(P_engine* engine); + bool d3_populate_validate_header(P_engine* engine); + bool d3_validate_types_define_cache(); + + /* Holds UIF sections that have no types. The key is the "sort" of section + * it is (i.e. 'header', 'engine_box'), and the value is the UIF node that + * corresponds to that section. */ + std::map untypedSections; + + /* Holds UIF sections that have types. The key is the "sort" of section it + * is, and the value is another map whose value is the "type" of the + * section, and whose value id the UIF node that corresponds to that + * section. */ + std::map> typedSections; + + /* Holds, for a (former) section, the type-specific section that defines + * how items created in the former section behave. The key is the section + * that defines the items, and the value is the section that defines the + * properties of that item. If no valid value exists, it is PNULL. This is + * just a convenient cache mechanism. Examples: + * + * 1. Given: + * + * [board(SomeBoardType)] + * +type=SomeMailboxType + * + * Then the first element of the map pair will be a pointer to the + * [board(SomeBoardType)] (section) UIF node, and the second will be a + * pointer to the [mailbox(SomeMailboxType)] (section) UIF node. + * + * 2. Given: + * + * [board(SomeBoardType)] + * ... // With no +type field + * [default_types] + * ... + * +mailbox_type=SomeMailboxType + * + * Then the entry in the map will be identical to case 1. + * + * 3. Given: + * + * [board(SomeBoardType)] + * +type=SomeMailboxType1 + * [default_types] + * ... + * +mailbox_type=SomeMailboxType2 + * + * Then the first element of the map pair will be a pointer to the + * [board(SomeBoardType)] (section) UIF node, and the second will be a + * pointer to the [mailbox(SomeMailboxType1)] (section) UIF node. + * + * 4. Given: + * + * [board(SomeBoardType)] + * ... // With no +type field + * [default_types] + * ... // With no +mailbox_type field + * + * Then the first element of the map pair will be a pointer to the + * [board(SomeBoardType)] (section) UIF node, and the second will be + * PNULL. Note that this is perfectly valid - just that each mailbox + * declaration must explicitly define a valid type. */ + std::map defaultTypes; + + /* Holds a default mailbox-mailbox cost, if defined. Relevant only for the + * current board. */ + float defaultMailboxMailboxCost = 0; + bool isDefaultMailboxCostDefined = false; + + /* Holds boards that have been declared to exist within a box in + * [engine_box], but which have not (yet) been created from parsing + * [engine_board]. */ + std::list undefinedBoards; + + /* Holds all boxes, indexable by their name. The key is the unique name of + * the box, and the value points to the box object itself. */ + std::map boxFromName; + +}; + +int how_many_digits(unsigned printable); +#endif diff --git a/Source/OrchBase/HardwareFileReader/HardwareFileReaderCore.cpp b/Source/OrchBase/HardwareFileReader/HardwareFileReaderCore.cpp new file mode 100644 index 00000000..ce5c263c --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/HardwareFileReaderCore.cpp @@ -0,0 +1,312 @@ +/* Defines behaviour for the core functionality (independent of dialect) of the + * hardware model file reader (see the accompanying header for further + * information). */ + +#include "HardwareFileReader.h" + +/* Constructs the file reader. */ +HardwareFileReader::HardwareFileReader():JNJ(),isFileLoaded(false) +{ + set_uif_error_callback(); +} + +/* Constructs the file reader, parses a file, and dumps it in an engine. */ +HardwareFileReader::HardwareFileReader(const char* filePath, P_engine* engine) + :JNJ(),isFileLoaded(false) +{ + set_uif_error_callback(); + load_file(filePath); + populate_hardware_model(engine); +} + +/* Assembles the error output string from 'Validation::errors', and writes it + * to the string at 'target'. */ +void HardwareFileReader::construct_error_output_string(std::string* target) +{ + /* If there aren't any errors, we get suspicious. If you're reading this + * having found a file that generates this error message, it means that one + * of the validation checks has failed without generating an error message + * (somehow). This is (almost) certainly a developer mistake. My advice + * would be to set a watch in gdb for the 'passedValidation' variable in + * d3_populate_hardware_model, and repeatedly step in to find the offending + * method. + * + * Good luck! */ + if (errors.size() == 0) + { + *target = dformat( + "Error(s) were encountered while loading the hardware description " + "file '%s', but we don't know what they are. Talk to an " + "Orchestrator developer (probably Mark).\n", loadedFile.c_str()); + } + + /* Otherwise, we're in expected territory for a file that's been + * read and contains mistakes. */ + else + { + /* Prepend error message with some useful information. */ + *target = dformat( + "Error(s) were encountered while loading the hardware description " + "file '%s'. Since the reader fails slowly, consider addressing " + "the top-most errors first:\n", loadedFile.c_str()); + + /* For each error, append it with an indentation and hyphen. */ + for (std::vector::iterator errorIterator = errors.begin(); + errorIterator != errors.end(); errorIterator++) + { + target->append(dformat(" - %s\n", errorIterator->c_str())); + } + } +} + + +/* Loads an input file and parses it through the UIF parsing mechanism to + * generate the tree structure. Arguments: + * + * - filePath: Path to the file to load + * + * If a file has already been loaded, it is unloaded before the new file is + * read. + * + * Throws if: + * + * - the input file does not exist. + * - the input file is syntactically invalid. */ +void HardwareFileReader::load_file(const char* filePath) +{ + if(!does_file_exist(filePath)) + { + throw HardwareFileNotFoundException( + dformat("[ERROR] No input file found at \"%s\".\n", filePath)); + } + + if (isFileLoaded) + { + Reset(); /* From UIF. */ + } + + loadedFile.clear(); + loadedFile += filePath; + isFileLoaded = true; + std::string filePathAsString = filePath; + Add(filePathAsString); +} + +/* Populates a POETS Engine with information from a previously-loaded hardware + * description file, while performing semantic validation. Arguments: + * + * - engine: Pointer to the POETS Engine to populate. Cleared if the input file + * is invalid + * + * Throws if: + * + * - no input file has been loaded by this reader. + * - the input file is semantically invalid. */ +void HardwareFileReader::populate_hardware_model(P_engine* engine) +{ + /* Throw if we have not loaded a file yet. */ + if (!isFileLoaded) + { + throw HardwareFileNotLoadedException(); + } + + /* Only dialect 1 and 3 for now (get_dialect errors if the dialect is not 1 + * or 3). */ + unsigned dialect; + try + { + dialect = get_dialect(); + } + catch (HardwareSemanticException &e) + { + delete engine; + throw; + } + + if (dialect == 1) + { + d1_populate_hardware_model(engine); + } + else if (dialect == 3) + { + d3_populate_hardware_model(engine); + } +} + +/* Returns true if a file exists at the path passed to by argument, and false + * otherwise. + * + * Would be more elegant and faster with POSIX's stat... + * + * Arguments: + * + * - filePath: Path to look at. */ +bool HardwareFileReader::does_file_exist(const char* filePath) +{ + FILE* testFile = fopen(filePath, "r"); + if (testFile) + { + fclose(testFile); + return true; + } + else{return false;} +} + +/* Returns the dialect of a loaded file. Throws if no input file has been + * loaded by this reader, or if the dialect is not defined correctly in the + * input file. */ +unsigned HardwareFileReader::get_dialect() +{ + /* Throw if we have not loaded a file yet. */ + if (!isFileLoaded) + { + throw HardwareFileNotLoadedException(); + } + + /* Get the header section node, and complain if there is not exactly one of + * them. */ + std::vector headerSections; + FndSect("header", headerSections); + if (headerSections.empty()) + { + throw HardwareSemanticException("[ERROR] No header section defined in " + "input file.\n"); + } + + else if (headerSections.size() > 1) + { + throw HardwareSemanticException( + dformat("[ERROR] %u header sections defined in input file (only " + "one is permitted).\n", headerSections.size())); + } + + /* Get the dialect entry, and complain if the dialect is not defined + * exactly once. */ + std::vector recordNodes; + FndRecdVari(headerSections[0], "dialect", recordNodes); + if (recordNodes.empty()) + { + throw HardwareSemanticException("[ERROR] No dialect defined in the " + "header section of the input file."); + } + + else if (recordNodes.size() > 1) + { + throw HardwareSemanticException( + dformat("[ERROR] The dialect field in the header section has been " + "defined %u times (only one is permitted).\n", + recordNodes.size())); + } + + /* Whine if the dialect field has no value. */ + std::vector valueNodes; + GetValu(recordNodes[0], valueNodes); + + if (valueNodes.empty()) + { + throw HardwareSemanticException( + dformat("[ERROR] Dialect definition at L%u has no value.", + recordNodes[0]->pos)); + } + + /* Whine if the dialect field has more than one value. If there is only one + * value, the first value node contains the value. If there are multiple + * (N) value nodes, the first value node contains N value nodes, each with + * an entry. */ + if (valueNodes[0]->leaf.size() != 0) + { + throw HardwareSemanticException( + dformat("[ERROR] Dialect definition at L%u has %u values, when " + "it should only have one.", + recordNodes[0]->pos, valueNodes[0]->leaf.size())); + } + + /* Whine if the dialect is not a natural number. */ + if(!is_node_value_natural(valueNodes[0])) + { + throw HardwareSemanticException( + dformat("[ERROR] Dialect definition at L%u has value '%s', which " + "is not 1 or 3.", + recordNodes[0]->pos, valueNodes[0]->str)); + } + + /* Whine if the dialect is not one or three. */ + int dialect = str2unsigned(valueNodes[0]->str); + if (dialect != 1 and dialect != 3) + { + throw HardwareSemanticException( + dformat("[ERROR] Only dialects 1 and 3 are currently supported " + "(the dialect definition at L%u has value '%s'.", + recordNodes[0]->pos, valueNodes[0]->str)); + } + + /* Whine if the dialect field does not have a "+" character preceeding the + * variable name. */ + std::vector variableNodes; + GetVari(recordNodes[0], variableNodes); + if (variableNodes[0]->qop != Lex::Sy_plus ) + { + throw HardwareSemanticException( + dformat("[ERROR] Dialect definition at L%u is missing the " + "preceeding '+' character.", recordNodes[0]->pos)); + } + + /* End of the gauntlet. */ + return dialect; +} + +/* Populates a vector with all values of a record, as strings. Only recurses + * one (or zero) level deep. Arguments: + * + * - toPopulate: Vector to fill with string values. + * - valueNode: Root value node. */ +void HardwareFileReader::get_values_as_strings( + std::vector* toPopulate, UIF::Node* valueNode) +{ + toPopulate->clear(); + std::vector::iterator leafIterator; + + /* If there's only one value, put it's string in the vector and call it a + * day. */ + if (valueNode->leaf.size() == 0) + { + toPopulate->push_back(valueNode->str); + } + + /* Otherwise, there are multiple values (hence multiple strings to put in + * the vector). */ + else + { + for (leafIterator=valueNode->leaf.begin(); + leafIterator!=valueNode->leaf.end(); leafIterator++) + { + toPopulate->push_back((*leafIterator)->str); + } + } +} + +/* Defines behaviour when a syntax error is encountered when reading a file. + * + * Basically, this just throws an exception with some debug messaging + * attached. See the example callback (UIF::DefECB) for more information, since + * this is lifted from there. + * + * Scoping be damned. */ +void HardwareFileReader::on_syntax_error(void* reader, void* uifInstance, int) +{ + std::string accumulatedMessage; + std::vector errorMessages; + static_cast(uifInstance)->Lx.Hst.Dump(errorMessages, 20); + accumulatedMessage = std::accumulate(errorMessages.begin(), + errorMessages.end(), std::string("")); + throw HardwareSyntaxException(dformat( + "[ERROR] Error in input hardware file syntax in \"%s\":\n%s", + static_cast(reader)->loadedFile.c_str(), + accumulatedMessage.c_str())); +} + +/* Registers a callback function for error handling purposes. */ +void HardwareFileReader::set_uif_error_callback() +{ + SetECB(this, on_syntax_error); /* From UIF. */ +} diff --git a/Source/OrchBase/HardwareFileManager/HardwareFileParser.cpp b/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect1.cpp similarity index 50% rename from Source/OrchBase/HardwareFileManager/HardwareFileParser.cpp rename to Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect1.cpp index a5e17879..c3821cb8 100644 --- a/Source/OrchBase/HardwareFileManager/HardwareFileParser.cpp +++ b/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect1.cpp @@ -1,288 +1,58 @@ -/* Defines behaviour for the hardware model file parser (see the accompanying - * header for further information). */ +/* Defines behaviour for the dialect 1 functionality of the hardware model file + * reader (see the accompanying header for further information). */ -#include "HardwareFileParser.h" +#include "HardwareFileReader.h" -/* Constructs the file parser. */ -HardwareFileParser::HardwareFileParser():JNJ(),isFileLoaded(false) +/* Writes an 'invalid variable' error message, and appends it to the error + * container. */ +void HardwareFileReader::d1_invalid_variable_message() { - set_uif_error_callback(); + errors.push_back(dformat( + "L%u: Variable '%s' in section '%s' is not recognised by the reader. " + "Is it valid?\n", record->pos, variable.c_str(), sectionName.c_str())); } -/* Constructs the file parser, parses a file, and dumps it in an engine. */ -HardwareFileParser::HardwareFileParser(const char* filePath, P_engine* engine) - :JNJ(),isFileLoaded(false) -{ - set_uif_error_callback(); - load_file(filePath); - populate_hardware_model(engine); -} - -/* Returns true if a file exists at the path passed to by argument, and false - * otherwise. - * - * Would be more elegant and faster with POSIX's stat... - * - * Arguments: - * - * - filePath: Path to look at. */ -bool HardwareFileParser::does_file_exist(const char* filePath) -{ - FILE* testFile = fopen(filePath, "r"); - if (testFile) - { - fclose(testFile); - return true; - } - else{return false;} -} - -/* Loads an input file and parses it through the UIF parsing mechanism to - * generate the tree structure. Arguments: - * - * - filePath: Path to the file to load - * - * If a file has already been loaded, it is unloaded before the new file is - * read. - * - * Throws if: - * - * - the input file does not exist. - * - the input file is syntactically invalid. */ -void HardwareFileParser::load_file(const char* filePath) -{ - if(!does_file_exist(filePath)) - { - throw HardwareFileNotFoundException( - dformat("[ERROR] No input file found at \"%s\".\n", filePath)); - } - - if (isFileLoaded) - { - Reset(); /* From UIF. */ - } - - loadedFile.clear(); - loadedFile += filePath; - isFileLoaded = true; - std::string filePathAsString = filePath; - Add(filePathAsString); -} - -/* Registers a callback function for error handling purposes. */ -void HardwareFileParser::set_uif_error_callback() -{ - SetECB(this, on_syntax_error); /* From UIF. */ -} - -/* Defines behaviour when a syntax error is encountered when reading a file. - * - * Basically, this just throws an exception with some debug messaging - * attached. See the example callback (UIF::DefECB) for more information, since - * this is lifted from there. - * - * Scoping be damned. */ -void HardwareFileParser::on_syntax_error(void* parser, void* uifInstance, int) -{ - std::string accumulatedMessage; - std::vector errorMessages; - static_cast(uifInstance)->Lx.Hst.Dump(errorMessages, 20); - accumulatedMessage = std::accumulate(errorMessages.begin(), - errorMessages.end(), std::string("")); - throw HardwareSyntaxException(dformat( - "[ERROR] Error in input hardware file syntax in \"%s\":\n%s", - static_cast(parser)->loadedFile.c_str(), - accumulatedMessage.c_str())); -} - -/* Populates a POETS Engine with information from the hardware - * model, after validation. Arguments: +/* Populates a POETS Engine with information from a previously-loaded hardware + * description file in dialect 1, after validating it. Arguments: * * - engine: Pointer to the POETS Engine to populate. Cleared if the input file * is valid. * * Throws if: * - * - no input file has been loaded by this parser. - * - if the input file is semantically invalid. */ -void HardwareFileParser::populate_hardware_model(P_engine* engine) + * - the input file is semantically invalid. */ +void HardwareFileReader::d1_populate_hardware_model(P_engine* engine) { - /* Throw if we have not loaded a file yet. */ - if (!isFileLoaded) - { - throw HardwareFileNotLoadedException(); - } - /* Call the various semantic validation methods. */ - std::string errorMessage; bool failedValidation = false; - failedValidation |= !validate_sections(&errorMessage); + failedValidation |= !d1_validate_sections(); /* Create a deployer (and do a little incidental validation...) */ Dialect1Deployer deployer; - failedValidation |= !provision_deployer(&deployer, &errorMessage); + failedValidation |= !d1_provision_deployer(&deployer); /* Throw if any of the validation methods failed. */ if (failedValidation) { - throw HardwareSemanticException(errorMessage.c_str()); + std::string allErrors; + construct_error_output_string(&allErrors); + throw HardwareSemanticException(allErrors.c_str()); } /* Otherwise, it's go time. */ deployer.deploy(engine); } -/* Validate that all sections have correct contents, given that all sections - * exist. The list of variables too great to enumerate here; look at the - * documentation or examples for the definitive list. - * - * Arguments: - * - * - errorMessage: string to write error message to, always appended to. - * - * Returns true if all mandatory fields are defined exactly once and if no - * incorrect variables are defined, and false otherwise. */ -bool HardwareFileParser::validate_section_contents(std::string* errorMessage) -{ - /* Define what is valid. */ - std::map mandatoryHeaderFields; - std::vector optionalHeaderFields; - mandatoryHeaderFields.insert(std::pair("datetime", 0)); - mandatoryHeaderFields.insert(std::pair("dialect", 0)); - mandatoryHeaderFields.insert(std::pair("version", 0)); - optionalHeaderFields.push_back("author"); - optionalHeaderFields.push_back("hardware"); - optionalHeaderFields.push_back("file"); - - /* */ - return true; -} - -/* Validate that there are no duplicated sections in the hardware model, and - * that the following mandatory section names are defined (in any order): - * - * - header, with an optional context-sensitive argument. - * - packet_address_format - * - engine - * - box - * - board - * - mailbox - * - core - * - * Arguments: - * - * - errorMessage: string to write error message to, always appended to. - * - * Returns true if all sections exist and are unique, and false - * otherwise. Should only be called after a file has been loaded. */ -bool HardwareFileParser::validate_sections(std::string* errorMessage) -{ - /* Grab section names. */ - std::vector sectionNameNodes; - GetNames(sectionNameNodes); - - /* Define valid node names, and counters for them (we're failing if any of - * these are not 1 at the end). */ - - std::map validNodeCounters; - std::map::iterator validNodeIterator; - validNodeCounters.insert(std::pair("header", 0)); - validNodeCounters.insert(std::pair - ("packet_address_format", 0)); - validNodeCounters.insert(std::pair("engine", 0)); - validNodeCounters.insert(std::pair("box", 0)); - validNodeCounters.insert(std::pair("board", 0)); - validNodeCounters.insert(std::pair("mailbox", 0)); - validNodeCounters.insert(std::pair("core", 0)); - - /* Store invalid node names for error reporting. */ - std::vector invalidNamedNodes; - std::vector::iterator invalidNamedNodeIterator; - - /* For each of the sections found in the file, get its name. If it's a - * valid section name, increment the counter. If it's not, push-back the - * node to invalidNamedNodes. */ - std::vector::iterator nodeIterator; - for (nodeIterator=sectionNameNodes.begin(); - nodeIterator!=sectionNameNodes.end(); nodeIterator++) - { - validNodeIterator = validNodeCounters.find((*nodeIterator)->str); - - /* We found it! Increment the counter. */ - if (validNodeIterator != validNodeCounters.end()) - { - validNodeIterator->second++; - } - /* We didn't find it! Add it to the list of invalid node names passed - * in. */ - else - { - invalidNamedNodes.push_back((*nodeIterator)); - } - } - - /* The input was valid if the validNodeCounters are all 1, and if there are - * no invalid node names. */ - bool returnValue = true; /* Success/failure state (true is a pass) */ - - /* Checking for invalid node names. */ - if (invalidNamedNodes.size() != 0) - { - /* We've failed. */ - returnValue = false; - - /* Let's let the user know how mean they are. */ - errorMessage->append("Sections with invalid names found in the input " - "file:\n"); - for (invalidNamedNodeIterator=invalidNamedNodes.begin(); - invalidNamedNodeIterator!=invalidNamedNodes.end(); - invalidNamedNodeIterator++) - { - errorMessage->append(dformat( - " %s (L%i)\n", - (*invalidNamedNodeIterator)->str.c_str(), - (*invalidNamedNodeIterator)->pos)); - } - } - - /* Checking all sections are defined exactly once. */ - bool headerPrinted = false; - for (validNodeIterator=validNodeCounters.begin(); - validNodeIterator!=validNodeCounters.end(); validNodeIterator++) - { - /* We didn't find the section. */ - if (validNodeIterator->second != 1) - { - /* We've failed. */ - returnValue = false; - if (!headerPrinted) - { - errorMessage->append("The following sections were not defined " - "exactly once:\n"); - headerPrinted = true; - } - - errorMessage->append(dformat(" %s, defined %u times.\n", - validNodeIterator->first.c_str(), - validNodeIterator->second)); - } - } - - return returnValue; -} - /* Provisions a dialect 1 deployer with the configuration in this - * parser. Performs validation on types of values in assignments, as well as + * reader. Performs validation on types of values in assignments, as well as * conveniently-easy-to-catch semantic mistakes (i.e. missing '+' signs before * a binding). Returns false if the validation found any problems, and true * otherwise. * * Arguments: * - * - deployer: Deployer object to provision. - * - errorMessage: String to append error messages to. */ -bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, - std::string* errorMessage) + * - deployer: Deployer object to provision. */ +bool HardwareFileReader::d1_provision_deployer(Dialect1Deployer* deployer) { bool valid = true; @@ -297,12 +67,10 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, std::vector valueNodes; std::vector::iterator valueNodeIterator; std::vector variableNodes; - std::string variable; /* For each section... */ std::vector recordNodes; std::vector::iterator recordIterator; - std::string sectionName; std::vector sectionNameNodes; std::vector values; for (sectionIterator=sectionNodes.begin(); @@ -333,9 +101,11 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, for (recordIterator=recordNodes.begin(); recordIterator!=recordNodes.end(); recordIterator++) { + record = *recordIterator; + /* Get the value and variable nodes. */ - GetVari((*recordIterator), variableNodes); - GetValu((*recordIterator), valueNodes); + GetVari(record, variableNodes); + GetValu(record, valueNodes); /* Ignore this record if the record has not got a variable/value pair (i.e. if the line is empty, or is just a comment). */ @@ -344,32 +114,16 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, /* Only the first variable node will be used, and it must have a * "+" preceeding it. */ variable = variableNodes[0]->str; - if (variableNodes[0]->qop != Lex::Sy_plus) + if (!complain_if_variable_not_plus_prefixed(variableNodes[0])) { valid = false; - errorMessage->append(dformat( + errors.push_back(dformat( "L%u: Your definition of variable '%s' is missing a '+' " - "character.\n", (*recordIterator)->pos, variable.c_str())); + "character.\n", record->pos, variable.c_str())); } - /* There may be many values. If there is only one value, the first - * value node contains the value. If there are multiple (N) value - * nodes, the first value node contains N value nodes, each with an - * entry. */ - values.clear(); - if (valueNodes[0]->leaf.size() == 0) /* There's only one value. */ - { - values.push_back(valueNodes[0]->str); - } - else /* There are many values. */ - { - for (valueNodeIterator=valueNodes[0]->leaf.begin(); - valueNodeIterator!=valueNodes[0]->leaf.end(); - valueNodeIterator++) - { - values.push_back((*valueNodeIterator)->str); - } - } + /* Get the values. */ + get_values_as_strings(&values, valueNodes[0]); /* A gigantic if/else for each section the record could be in, * followed by a slightly less gigantic if/else for each variable @@ -389,11 +143,11 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, if (valueNodes[0]->qop != Lex::Sy_ISTR) { valid = false; - errorMessage->append(dformat( + errors.push_back(dformat( "L%u: Variable '%s' in section '%s' has value " "'%s', which is not a datetime in the form " "YYYYMMDDhhmmss.\n", - (*recordIterator)->pos, variable.c_str(), + record->pos, variable.c_str(), sectionName.c_str(), values[0].c_str())); } deployer->datetime = str2long(values[0]); @@ -406,22 +160,12 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, { deployer->version = values[0]; } - else if (variable == "dialect") - { - if (values[0] != "1") - { - valid = false; - errorMessage->append(dformat( - "L%u: Only dialect 1 is currently supported.\n", - (*recordIterator)->pos)); - } - } - else if (variable == "hardware"); + else if (variable == "dialect"); /* Read elsewhere. */ + else if (variable == "hardware"); /* Ignored, for now. */ else { valid = false; - invalid_variable_message(errorMessage, (*recordIterator), - sectionName, variable); + d1_invalid_variable_message(); } } @@ -429,9 +173,8 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, { if (variable == "board") { - valid &= are_values_at_node_natural( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_values_and_children_not_natural( + valueNodes[0]); /* Convert the values from strings to uints, and drop them * in the deployer. */ @@ -442,34 +185,19 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, } else if (variable == "box") { - valid &= is_value_at_node_natural( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); - if (valueNodes[0]->qop != Lex::Sy_ISTR) - { - valid = false; - errorMessage->append(dformat( - "L%u: Variable '%s' in section '%s' has value " - "'%s', which is not a natural number.\n", - (*recordIterator)->pos, variable.c_str(), - sectionName.c_str(), values[0].c_str())); - } + valid &= complain_if_value_not_natural(valueNodes[0]); deployer->boxWordLength = str2unsigned(values[0]); } else if (variable == "core") { - valid &= is_value_at_node_natural( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_natural(valueNodes[0]); deployer->coreWordLength = str2unsigned(values[0]); } else if (variable == "mailbox") { /* Same as with packet_address_format::board. */ - valid &= are_values_at_node_natural( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); - + valid &= complain_if_values_and_children_not_natural( + valueNodes[0]); deployer->mailboxWordLengths.resize(values.size()); std::transform(values.begin(), values.end(), deployer->mailboxWordLengths.begin(), @@ -477,16 +205,13 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, } else if (variable == "thread") { - valid &= is_value_at_node_natural( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_natural(valueNodes[0]); deployer->threadWordLength = str2unsigned(values[0]); } else { valid = false; - invalid_variable_message(errorMessage, (*recordIterator), - sectionName, variable); + d1_invalid_variable_message(); } } @@ -494,18 +219,14 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, { if (variable == "boxes") { - valid &= is_value_at_node_natural( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_natural(valueNodes[0]); deployer->boxesInEngine = str2unsigned(values[0]); } else if (variable == "boards") { if (values.size() == 1) /* Scalar */ { - valid &= is_value_at_node_natural( - (*recordIterator), valueNodes[0], variable, - values[0], sectionName, errorMessage); + valid &= complain_if_value_not_natural(valueNodes[0]); deployer->boardsInEngine.push_back( str2unsigned(values[0])); deployer->boardHypercubePeriodicity.push_back(false); @@ -517,17 +238,15 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, if (valueNodes[0]->str != "hypercube") { valid = false; - errorMessage->append(dformat( + errors.push_back(dformat( "L%u: The value of the 'boards' variable in " "the engine section is multidimensional, and " "so should be a 'hypercube'.\n", - (*recordIterator)->pos)); + record->pos)); } - valid &= are_values_at_node_natural( - (*recordIterator), valueNodes[0], variable, - values[0], sectionName, errorMessage); - + valid &= complain_if_values_and_children_not_natural( + valueNodes[0]); deployer->boardsAsHypercube = true; deployer->boardsInEngine.resize(values.size()); std::transform(values.begin(), values.end(), @@ -552,10 +271,10 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, else { valid = false; - errorMessage->append(dformat( + errors.push_back(dformat( "L%u: Invalid value for boards variable " "in the engine section.\n", - (*recordIterator)->pos)); + record->pos)); deployer->boardHypercubePeriodicity. push_back(false); } @@ -564,23 +283,18 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, } else if (variable == "external_box_cost") { - valid &= is_value_at_node_floating( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_floating(valueNodes[0]); deployer->costExternalBox = str2float(values[0]); } else if (variable == "board_board_cost") { - valid &= is_value_at_node_floating( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_floating(valueNodes[0]); deployer->costBoardBoard = str2float(values[0]); } else { valid = false; - invalid_variable_message(errorMessage, (*recordIterator), - sectionName, variable); + d1_invalid_variable_message(); } } @@ -589,20 +303,18 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, { if (variable == "box_board_cost") { - valid &= is_value_at_node_floating( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_floating(valueNodes[0]); deployer->costBoxBoard = str2float(values[0]); } else if (variable == "supervisor_memory") { + valid &= complain_if_value_not_natural(valueNodes[0]); deployer->boxSupervisorMemory = str2unsigned(values[0]); } else { valid = false; - invalid_variable_message(errorMessage, (*recordIterator), - sectionName, variable); + d1_invalid_variable_message(); } } @@ -623,11 +335,11 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, if (valueNodes[0]->str != "hypercube") { valid = false; - errorMessage->append(dformat( + errors.push_back(dformat( "L%u: The value of the 'mailboxes' variable " "in the board section is multidimensional, " "and so should be a 'hypercube'.\n", - (*recordIterator)->pos)); + record->pos)); } deployer->mailboxesAsHypercube = true; @@ -654,10 +366,10 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, else { valid = false; - errorMessage->append(dformat( + errors.push_back(dformat( "L%u: Invalid value for mailboxes " "variable in the board section.\n", - (*recordIterator)->pos)); + record->pos)); deployer->mailboxHypercubePeriodicity. push_back(false); } @@ -666,37 +378,28 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, } else if (variable == "board_mailbox_cost") { - valid &= is_value_at_node_floating( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_floating(valueNodes[0]); deployer->costBoardMailbox = str2float(values[0]); } else if (variable == "mailbox_mailbox_cost") { - valid &= is_value_at_node_floating( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_floating(valueNodes[0]); deployer->costBoardMailbox = str2float(values[0]); } else if (variable == "supervisor_memory") { - valid &= is_value_at_node_natural( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_natural(valueNodes[0]); deployer->boardSupervisorMemory = str2unsigned(values[0]); } else if (variable == "dram") { - valid &= is_value_at_node_natural( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_natural(valueNodes[0]); deployer->dram = str2unsigned(values[0]); } else { valid = false; - invalid_variable_message(errorMessage, (*recordIterator), - sectionName, variable); + d1_invalid_variable_message(); } } @@ -704,30 +407,23 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, { if (variable == "cores") { - valid &= is_value_at_node_natural( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_natural(valueNodes[0]); deployer->coresInMailbox = str2unsigned(values[0]); } else if (variable == "mailbox_core_cost") { - valid &= is_value_at_node_floating( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_floating(valueNodes[0]); deployer->costMailboxCore = str2float(values[0]); } else if (variable == "core_core_cost") { - valid &= is_value_at_node_floating( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_floating(valueNodes[0]); deployer->costCoreCore = str2float(values[0]); } else { valid = false; - invalid_variable_message(errorMessage, (*recordIterator), - sectionName, variable); + d1_invalid_variable_message(); } } @@ -735,52 +431,41 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, { if (variable == "threads") { - valid &= is_value_at_node_natural( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_natural(valueNodes[0]); deployer->threadsInCore = str2unsigned(values[0]); } else if (variable == "instruction_memory") { - valid &= is_value_at_node_natural( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_natural(valueNodes[0]); deployer->instructionMemory = str2unsigned(values[0]); } else if (variable == "data_memory") { - valid &= is_value_at_node_natural( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_natural(valueNodes[0]); deployer->dataMemory = str2unsigned(values[0]); } else if (variable == "core_thread_cost") { - valid &= is_value_at_node_floating( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_floating(valueNodes[0]); deployer->costCoreThread = str2float(values[0]); } else if (variable == "thread_thread_cost") { - valid &= is_value_at_node_floating( - (*recordIterator), valueNodes[0], variable, values[0], - sectionName, errorMessage); + valid &= complain_if_value_not_floating(valueNodes[0]); deployer->costThreadThread = str2float(values[0]); } else { valid = false; - invalid_variable_message(errorMessage, (*recordIterator), - sectionName, variable); + d1_invalid_variable_message(); } } else { valid = false; - errorMessage->append(dformat( - "L%u: Section name '%s' is not recognised by the parser. " + errors.push_back(dformat( + "L%u: Section name '%s' is not recognised by the reader. " "Is it valid?\n", (*sectionIterator)->pos, sectionName.c_str())); } @@ -790,116 +475,112 @@ bool HardwareFileParser::provision_deployer(Dialect1Deployer* deployer, return valid; } -/* Converts a float-like input string to an actual float. */ -float str2float(std::string floatLike) +/* Validate that there are no duplicated sections in the hardware description + * input file, and that the following mandatory section names are defined (in + * any order): + * + * - header (with an optional context-sensitive argument) + * - packet_address_format + * - engine + * - box + * - board + * - mailbox + * - core + * + * Returns true if all sections exist and are unique, and false + * otherwise. Should only be called after a file has been loaded. */ +bool HardwareFileReader::d1_validate_sections() { - float returnValue; - sscanf(floatLike.c_str(), "%f", &returnValue); - return returnValue; -} + /* Grab section names. */ + std::vector sectionNameNodes; + GetNames(sectionNameNodes); -/* Converts a float-like input string to an actual float. I know something like - * this exists in flat, but I want something with a single argument so that I - * can use std::transform to transform a vector of strings into a vector of - * unsigneds. */ -unsigned str2unsigned(std::string uintLike) -{ - unsigned returnValue; - sscanf(uintLike.c_str(), "%u", &returnValue); - return returnValue; -} + /* Define valid node names, and counters for them (we're failing if any of + * these are not 1 at the end). */ + std::map validNodeCounters; + std::map::iterator validNodeIterator; + validNodeCounters.insert(std::pair("header", 0)); + validNodeCounters.insert(std::pair + ("packet_address_format", 0)); + validNodeCounters.insert(std::pair("engine", 0)); + validNodeCounters.insert(std::pair("box", 0)); + validNodeCounters.insert(std::pair("board", 0)); + validNodeCounters.insert(std::pair("mailbox", 0)); + validNodeCounters.insert(std::pair("core", 0)); -/* Returns whether the value at a node is a natural number. Arguments: - * - * - recordNode: Record parent node (for writing error message). - * - valueNode: The UIF node that holds the value. - * - variable: Variable name string (for writing error message). - * - value: Value string (for writing error message). - * - sectionName: The name of the section the record lives in (for writing - error message). - * - errorMessage: String to append the error message to, if any. */ -bool HardwareFileParser::is_value_at_node_natural( - UIF::Node* recordNode, UIF::Node* valueNode, std::string variable, - std::string value, std::string sectionName, std::string* errorMessage) -{ - if (valueNode->qop != Lex::Sy_ISTR) + /* Store invalid node names for error reporting. */ + std::vector invalidNamedNodes; + std::vector::iterator invalidNamedNodeIterator; + + /* For each of the sections found in the file, get its name. If it's a + * valid section name, increment the counter. If it's not, push-back the + * node to invalidNamedNodes. */ + std::vector::iterator nodeIterator; + for (nodeIterator=sectionNameNodes.begin(); + nodeIterator!=sectionNameNodes.end(); nodeIterator++) { - errorMessage->append(dformat( - "L%u: Variable '%s' in section '%s' has value '%s', which is not " - "a natural number.\n", - recordNode->pos, variable.c_str(), sectionName.c_str(), - value.c_str())); - return false; + validNodeIterator = validNodeCounters.find((*nodeIterator)->str); + + /* We found it! Increment the counter. */ + if (validNodeIterator != validNodeCounters.end()) + { + validNodeIterator->second++; + } + /* We didn't find it! Add it to the list of invalid node names passed + * in. */ + else + { + invalidNamedNodes.push_back((*nodeIterator)); + } } - return true; -} -/* Returns whether the value at a node is a positive floating-point - * number (or an integer). Arguments: - * - * - recordNode: Record parent node (for writing error message). - * - valueNode: The UIF node that holds the value. - * - variable: Variable name string (for writing error message). - * - value: Value string (for writing error message). - * - sectionName: The name of the section the record lives in (for writing - error message). - * - errorMessage: String to append the error message to, if any. */ -bool HardwareFileParser::is_value_at_node_floating( - UIF::Node* recordNode, UIF::Node* valueNode, std::string variable, - std::string value, std::string sectionName, std::string* errorMessage) -{ - if (valueNode->qop != Lex::Sy_FSTR && valueNode->qop != Lex::Sy_ISTR) + /* The input was valid if the validNodeCounters are all 1, and if there are + * no invalid node names. */ + bool returnValue = true; /* Success/failure state (true is a pass) */ + + /* Checking for invalid node names. */ + if (invalidNamedNodes.size() != 0) { - errorMessage->append(dformat( - "L%u: Variable '%s' in section '%s' has value '%s', which is not " - "a positive floating-point number or a positive integer.\n", - recordNode->pos, variable.c_str(), sectionName.c_str(), - value.c_str())); - return false; + /* We've failed. */ + returnValue = false; + + /* Let's let the user know how mean they are. */ + std::string staging; + errors.push_back("Sections with invalid names found in the input " + "file:\n"); + for (invalidNamedNodeIterator=invalidNamedNodes.begin(); + invalidNamedNodeIterator!=invalidNamedNodes.end(); + invalidNamedNodeIterator++) + { + errors.push_back(dformat( + " %s (L%i)\n", + (*invalidNamedNodeIterator)->str.c_str(), + (*invalidNamedNodeIterator)->pos)); + } } - return true; -} -/* Returns whether the value at a node, and all of its children, are natural - * numbers. Arguments: - * - * - recordNode: Record parent node (for writing error message). - * - valueNode: The UIF node that holds the value. - * - variable: Variable name string (for writing error message). - * - value: Value string (for writing error message). - * - sectionName: The name of the section the record lives in (for writing - error message). - * - errorMessage: String to append the error message to, if any. */ -bool HardwareFileParser::are_values_at_node_natural( - UIF::Node* recordNode, UIF::Node* valueNode, std::string variable, - std::string value, std::string sectionName, std::string* errorMessage) -{ - std::vector::iterator valueNodeIterator; - bool valid = true; - for (valueNodeIterator=valueNode->leaf.begin(); - valueNodeIterator!=valueNode->leaf.end(); valueNodeIterator++) + /* Checking all sections are defined exactly once. */ + bool headerPrinted = false; + for (validNodeIterator=validNodeCounters.begin(); + validNodeIterator!=validNodeCounters.end(); validNodeIterator++) { - valid &= is_value_at_node_natural( - recordNode, (*valueNodeIterator), - variable, (*valueNodeIterator)->str.c_str(), - sectionName.c_str(), errorMessage); + /* We didn't find the section. */ + if (validNodeIterator->second != 1) + { + /* We've failed. */ + returnValue = false; + if (!headerPrinted) + { + errors.push_back("The following sections were not defined " + "exactly once:\n"); + headerPrinted = true; + } + + errors.push_back(dformat(" %s, defined %u times.\n", + validNodeIterator->first.c_str(), + validNodeIterator->second)); + } } - return valid; -} -/* Writes an 'invalid variable' error message, and appends it to a - * string. Arguments: - * - * - errorMessage: String to append to. - * - recordNode: Record node where the invalid variable was found. - * - sectionName: Name of the section in which the invalid variable was found. - * - variable: Invalid variable name. */ -void HardwareFileParser::invalid_variable_message( - std::string* errorMessage, UIF::Node* recordNode, std::string sectionName, - std::string variable) -{ - errorMessage->append(dformat( - "L%u: Variable '%s' in section '%s' is not recognised by the parser. " - "Is it valid?\n", - recordNode->pos, variable.c_str(), sectionName.c_str())); + return returnValue; } diff --git a/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3.cpp b/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3.cpp new file mode 100644 index 00000000..cf089a6e --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3.cpp @@ -0,0 +1,63 @@ +/* Defines behaviour for the dialect 3 functionality of the hardware model file + * reader (see the accompanying header for further information). */ + +#include "HardwareFileReader.h" + +/* Catastrophic failure, we out, yo. Well, "catastrophic" is perhaps taking it + * a bit far. My point is, this error is unrecoverable from the perspective of + * the file reader. + * + * This method constructs an error output string, puts it in the message of an + * exception, then tosses it over the fence. Arguments: + * + * - engine: Partially-populated unsafe engine. */ +void HardwareFileReader::d3_catastrophic_failure(P_engine* engine) +{ + delete engine; + std::string allErrors; + construct_error_output_string(&allErrors); + + /* Lob over the fence. */ + throw HardwareSemanticException(allErrors.c_str()); +} + +/* Populates a POETS Engine with information from a previously-loaded hardware + * description file in dialect 3, after validating it. Arguments: + * + * - engine: Pointer to the POETS Engine to populate. Cleared if the input file + * is valid. + * + * Throws if: + * + * - the input file is semantically invalid. */ +void HardwareFileReader::d3_populate_hardware_model(P_engine* engine) +{ + /* Check sections are defined correctly. Not much point continuing if they + * are not defined correctly. */ + if (!d3_load_validate_sections()){d3_catastrophic_failure(engine);} + + /* Populate the engine with information from the header section. */ + bool passedValidation = true; + passedValidation &= d3_populate_validate_header(engine); + + /* Populate the hardware address format owned by the engine. */ + passedValidation &= d3_populate_validate_address_format(engine); + + /* Verify that default types, and type fields in sections, are valid, and + * map to sections that exist. */ + passedValidation &= d3_validate_types_define_cache(); + + /* We can't go any further than this without risking a segfault, because if + * the address format is not defined properly, item address assignment will + * fail badly. */ + if (!passedValidation){d3_catastrophic_failure(engine);} + + /* Boxes */ + passedValidation &= d3_populate_validate_engine_box(engine); + + /* Boards (and the items beneath) */ + passedValidation &= d3_populate_validate_engine_board_and_below(engine); + + /* Were there any errors? */ + if (!passedValidation){d3_catastrophic_failure(engine);} +} diff --git a/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Engine.cpp b/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Engine.cpp new file mode 100644 index 00000000..ece9951f --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Engine.cpp @@ -0,0 +1,329 @@ +/* Defined behaviour for the dialect 3 functionality of methods that directly + * modify either the P_engine object, or its address format. */ + +#include "HardwareFileReader.h" + +/* Validate the contents of the packet_address_format section, and populate the + * format object in the engine with them. + * + * Returns true if all validation checks pass, and false otherwise. Arguments: + * + * - engine: Engine to populate */ +bool HardwareFileReader::d3_populate_validate_address_format(P_engine* engine) +{ + bool anyErrors = false; /* Innocent until proven guilty. */ + sectionName = "packet_address_format"; + + /* Valid fields for the header section. */ + std::vector validFields; + std::vector::iterator fieldIterator; + validFields.push_back("box"); + validFields.push_back("board"); + validFields.push_back("mailbox"); + validFields.push_back("core"); + validFields.push_back("thread"); + + /* Mandatory fields for the header section. Could be indeces that refer to + * validFields, but this is (probably) easier to read/maintain. */ + std::vector mandatoryFields; + mandatoryFields.push_back("board"); + mandatoryFields.push_back("mailbox"); + mandatoryFields.push_back("core"); + mandatoryFields.push_back("thread"); + + /* We live in a world where the box component of the address may or may not + * be mandatory. */ + if (!IGNORE_BOX_ADDRESS_COMPONENT) + { + mandatoryFields.push_back("box"); + } + + /* Holds fields we've already grabbed (for validation purposes). */ + std::map fieldsFound; + for (fieldIterator=validFields.begin(); fieldIterator!=validFields.end(); + fieldIterator++) + { + fieldsFound.insert(std::make_pair(*fieldIterator, false)); + } + + /* Staging vectors for holding value nodes and variable nodes. */ + std::vector valueNodes; + std::vector variableNodes; + + /* Staging area for the values of multi-valued records (we need to add them + * together). */ + std::vector values; + std::vector::iterator valueIterator; + unsigned* accumulationTarget; /* This will point to a _WordLength member + * of the address format object owned by the + * engine. */ + + /* Iterate through all record nodes in this section. */ + std::vector recordNodes; + GetRecd(untypedSections[sectionName], recordNodes); + + std::vector::iterator recordIterator; + for (recordIterator=recordNodes.begin(); + recordIterator!=recordNodes.end(); recordIterator++) + { + record = *recordIterator; + + /* Get the value and variable nodes. */ + GetVari(record, variableNodes); + GetValu(record, valueNodes); + + /* Ignore this record if the record has not got a variable/value + * pair (i.e. if the line is empty, or is just a comment). */ + if (variableNodes.size() == 0 and valueNodes.size() == 0){continue;} + + /* Complaints */ + if (!(complain_if_variable_not_plus_prefixed(variableNodes[0]) and + complain_if_variable_name_invalid(variableNodes[0], + &validFields) and + complain_if_record_is_multivariable(&variableNodes))) + { + anyErrors = true; + continue; + } + + /* Complain if duplicate. NB: We know the variable name is valid if + * control has reached here. */ + if (complain_if_variable_true_in_map(variableNodes[0], &fieldsFound)) + { + anyErrors = true; + continue; + } + variable = variableNodes[0]->str; + fieldsFound[variable] = true; + + /* Specific logic for each variable. For box, core, and thread complain + * if (in order): + * + * - The record is multi-valued. + * - The value is not natural. */ + if (variable == "box" or variable == "core" or + variable == "thread") + { + + /* Complaints */ + if (!(complain_if_record_is_multivalue(&valueNodes) and + complain_if_value_not_natural(valueNodes[0]))) + { + anyErrors = true; + continue; + } + + /* We bind! */ + if (variable == "box") + { + engine->addressFormat.boxWordLength = + str2unsigned(valueNodes[0]->str); + } + else if (variable == "core") + { + engine->addressFormat.coreWordLength = + str2unsigned(valueNodes[0]->str); + } + else /* Must be thread */ + { + engine->addressFormat.threadWordLength = + str2unsigned(valueNodes[0]->str); + } + } + + /* For board and mailbox, complain if any of the values are not + * natural. */ + else if (variable == "board" or variable == "mailbox") + { + /* Validate, treating the single-variable and multiple-variable + * cases separately. */ + get_values_as_strings(&values, valueNodes[0]); + if (values.size() == 1) + { + if (!complain_if_value_not_natural(valueNodes[0])) + { + anyErrors = true; + continue; + } + } + else + { + if (!complain_if_values_and_children_not_natural( + valueNodes[0])) + { + anyErrors = true; + continue; + } + } + + /* We bind! (is either board or mailbox) */ + + /* Determine target */ + accumulationTarget = + &(variable == "board" ? + engine->addressFormat.boardWordLength : + engine->addressFormat.mailboxWordLength); + *accumulationTarget = 0; /* Reset target */ + + /* Add values to target. The total word length is (simply) the sum + * of its multidimensional constituents. */ + for (valueIterator=values.begin(); valueIterator!=values.end(); + valueIterator++) + { + *accumulationTarget += str2unsigned(*valueIterator); + } + } + } + + /* Ensure mandatory fields have been defined. */ + if (!complain_if_mandatory_field_not_defined(&mandatoryFields, + &fieldsFound)) + { + anyErrors = true; + } + + return !anyErrors; +} + +/* Validate the contents of the header section, and populate an engine with + * them. + * + * Returns true if all validation checks pass, and false otherwise. Arguments: + * + * - engine: Engine to populate */ +bool HardwareFileReader::d3_populate_validate_header(P_engine* engine) +{ + bool anyErrors = false; /* Innocent until proven guilty. */ + sectionName = "header"; + + /* Valid fields for the header section. */ + std::vector validFields; + std::vector::iterator fieldIterator; + validFields.push_back("author"); + validFields.push_back("datetime"); + validFields.push_back("dialect"); + validFields.push_back("file"); + validFields.push_back("hardware"); + validFields.push_back("version"); + + /* Mandatory fields for the header section. Could be indeces that refer to + * validFields, but this is (probably) easier to read/maintain. */ + std::vector mandatoryFields; + mandatoryFields.push_back("datetime"); + mandatoryFields.push_back("dialect"); + mandatoryFields.push_back("version"); + + /* Holds fields we've already grabbed (for validation purposes). */ + std::map fieldsFound; + for (fieldIterator=validFields.begin(); fieldIterator!=validFields.end(); + fieldIterator++) + { + fieldsFound.insert(std::make_pair(*fieldIterator, false)); + } + + /* Temporary staging vectors for holding value nodes and variable + * nodes. */ + std::vector valueNodes; + std::vector variableNodes; + + /* Holds whether or not the engine has been given a name. */ + bool isEngineNamed = false; + + /* Iterate through all record nodes in this section. */ + std::vector recordNodes; + GetRecd(untypedSections[sectionName], recordNodes); + + for (std::vector::iterator recordIterator=recordNodes.begin(); + recordIterator!=recordNodes.end(); recordIterator++) + { + record = *recordIterator; + + /* Get the value and variable nodes. */ + GetVari(record, variableNodes); + GetValu(record, valueNodes); + + /* Ignore this record if the record has not got a variable/value + * pair (i.e. if the line is empty, or is just a comment). */ + if (variableNodes.size() == 0 and valueNodes.size() == 0){continue;} + + /* Complaints */ + if (!(complain_if_variable_not_plus_prefixed(variableNodes[0]) and + complain_if_variable_name_invalid(variableNodes[0], + &validFields) and + complain_if_record_is_multivariable(&variableNodes) and + complain_if_record_is_multivalue(&valueNodes))) + { + anyErrors = true; + continue; + } + + /* Complain if duplicate. NB: We know the variable name is valid if + * control has reached here. */ + if (complain_if_variable_true_in_map(variableNodes[0], &fieldsFound)) + { + anyErrors = true; + continue; + } + variable = variableNodes[0]->str; + fieldsFound[variable] = true; + + /* Specific logic for each variable. */ + if (variable == "author") + { + engine->author = valueNodes[0]->str; + } + + else if (variable == "datetime") + { + /* Special validation for this one. */ + if (valueNodes[0]->qop != Lex::Sy_ISTR) + { + errors.push_back(dformat( + "L%u: Variable '%s' in the '%s' section has value '%s', " + "which is not a datetime in the form YYYYMMDDhhmmss.", + record->pos, variable.c_str(), + valueNodes[0]->str.c_str(), sectionName.c_str())); + anyErrors = true; + } + + else + { + engine->datetime = str2long(valueNodes[0]->str); + } + + } + + /* This has already been read and validated, so we don't care. */ + else if (variable == "dialect"); + + else if (variable == "file") + { + engine->fileOrigin = valueNodes[0]->str; + engine->Name(valueNodes[0]->str); + isEngineNamed = true; + } + + /* Ignore this one for now. */ + else if (variable == "hardware"){} + + else if (variable == "version") + { + engine->version = valueNodes[0]->str; + } + } + + /* Ensure mandatory fields have been defined. */ + if (!complain_if_mandatory_field_not_defined(&mandatoryFields, + &fieldsFound)) + { + anyErrors = true; + } + + /* If the engine has not been named, give it a default name. */ + if (!isEngineNamed) + { + engine->Name(DEFAULT_ENGINE_NAME); + } + + return !anyErrors; +} diff --git a/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Getters.cpp b/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Getters.cpp new file mode 100644 index 00000000..6ee75211 --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Getters.cpp @@ -0,0 +1,357 @@ +/* Defines behaviour for the dialect 3 functionality of methods that get + * attributes from items (either variables or values) from the UIF data + * structure. */ + +#include "HardwareFileReader.h" + +/* Extract the address (addr) from an item definition. + * + * Concatenates and drops the address into 'address', and sets it to zero if + * there was no address defined. Does no validation (which is a mistake, but + * I'm tight for time). Returns true if an address was found, and false + * otherwise. Arguments: + * + * - itemNode: Variable node to extract the type from. + * - address: Integer to write to. */ +bool HardwareFileReader::d3_get_address_from_item_definition( + UIF::Node* itemNode, AddressComponent* address) +{ + std::string fullBinary = ""; + *address = 0; + + /* Get the "addr" field from the item, if any. */ + std::vector::iterator leafIterator = itemNode->leaf.begin(); + while (leafIterator!=itemNode->leaf.end()) + { + if ((*leafIterator)->str == "addr"){break;} + leafIterator++; + } + + /* If we didn't find it, return. */ + if (leafIterator == itemNode->leaf.end()){return false;} + + /* Is there actually an address in there? If not, return. */ + if ((*leafIterator)->leaf.size() == 0){return false;} + + /* Grab and concatenate, assuming they're all innocent (no time for + * validation). */ + std::vector::iterator lLeafIterator; /* Sorry */ + for (lLeafIterator=(*leafIterator)->leaf.begin(); + lLeafIterator!=(*leafIterator)->leaf.end(); lLeafIterator++) + { + fullBinary.append((*lLeafIterator)->str); + } + + *address = std::stoi(fullBinary.c_str(), 0, 2); + return true; +} + +/* Extract the compound board name from a node, and validate it. Returns true + * if valid, and false if invalid or if the box is missing (while writing an + * angry letter to your manager). + * + * Board names are compound names, with box and board components. Arguments: + * + * - itemNode: The node (variable, or value). + * - boardName: BoardName to populate. */ +bool HardwareFileReader::d3_get_board_name( + UIF::Node* itemNode, BoardName* boardName) +{ + /* Clear the board name. */ + boardName->first = ""; + boardName->second = ""; + + /* Get the box component, and verify a box exists with that name + * already. */ + std::string boxName = itemNode->str; + std::map::iterator boxNameFinder; + boxNameFinder = boxFromName.find(boxName); + if(boxNameFinder == boxFromName.end()) + { + errors.push_back(dformat( + "L%u: Box component of board name '%s' at C%u does not correspond " + "to an existing box.", + record->pos, boxName.c_str(), itemNode->pos)); + return false; + } + + /* Get the board component, through some dilligent searching... */ + std::vector::iterator leafIterator; + for(leafIterator=itemNode->leaf.begin(); + leafIterator!=itemNode->leaf.end(); leafIterator++) + { + if ((*leafIterator)->str == "board"){break;} + } + + if (leafIterator == itemNode->leaf.end()) + { + errors.push_back(dformat( + "L%u: Couldn't find a board field in this board definition near " + "C%u (so I don't know what the name is).", + record->pos, itemNode->pos)); + return false; + } + + /* Does the board component have zero or multiple values? If so, that's not + * valid. */ + if ((*leafIterator)->leaf.size() != 1) + { + errors.push_back(dformat("L%u: Multiple board components defined for " + "the name of this board (only one is " + "allowed).", record->pos)); + return false; + } + + /* Is the board-component of the name valid? */ + if (!complain_if_variable_not_a_valid_item_name((*leafIterator)->leaf[0])) + { + return false; + } + + /* All good, assign and return. */ + boardName->first = boxName; + boardName->second = (*leafIterator)->leaf[0]->str; + return true; +} + +/* Extract the cost from an edge definition. + * + * Drops the value into 'cost', and sets it to -1 if there was no cost defined, + * or if the cost was invalid. Returns true if a cost was found, and false + * otherwise. Arguments: + * + * - itemNode: Value node to extract the edge cost from. + * - cost: String to write the type to. */ +bool HardwareFileReader::d3_get_explicit_cost_from_edge_definition( + UIF::Node* itemNode, float* cost) +{ + *cost = -1; + + /* Get the "cost" field from the item, if any. */ + std::vector::iterator leafIterator = itemNode->leaf.begin(); + while (leafIterator!=itemNode->leaf.end()) + { + if ((*leafIterator)->str == "cost"){break;} + leafIterator++; + } + + /* If we didn't find it, return. */ + if (leafIterator == itemNode->leaf.end()){return false;} + + /* Is there actually a cost in there? If not, return. */ + if ((*leafIterator)->leaf.size() == 0){return false;} + + /* Is the first entry a float? Leave with true otherwise (the value was + * found, it just was not valid). */ + if (!is_node_value_floating((*leafIterator)->leaf[0])){return true;} + + /* Bind the cost with the first entry. */ + *cost = str2float((*leafIterator)->leaf[0]->str); + return true; +} + +/* Extract the type from an item definition. + * + * Drops the type into 'type', and clears it if there is no type defined. Does + * no validation. Returns true if a type was found, and false + * otherwise. Arguments: + * + * - itemNode: Variable node to extract the type from. + * - type: String to write the type to. */ +bool HardwareFileReader::d3_get_explicit_type_from_item_definition( + UIF::Node* itemNode, std::string* type) +{ + type->clear(); + + /* Get the "type" field from the item, if any. */ + std::vector::iterator leafIterator = itemNode->leaf.begin(); + while (leafIterator!=itemNode->leaf.end()) + { + if ((*leafIterator)->str == "type"){break;} + leafIterator++; + } + + /* If we didn't find it, return. */ + if (leafIterator == itemNode->leaf.end()){return false;} + + /* Is there actually a type in there? If not, return. */ + if ((*leafIterator)->leaf.size() == 0){return false;} + + /* Bind the type with the first entry. */ + *type = (*leafIterator)->leaf[0]->str; + return true; +} + +/* Extract the (non-compound) mailbox name from a node, and validate it. Is + * analogous to d3_get_board_name. + * + * Returns true if valid, and false if invalid while writing to the error* + * string. Arguments: + * + * - itemNode: The node (variable, or value). + * - mailboxName: MailboxName to populate. */ +bool HardwareFileReader::d3_get_mailbox_name(UIF::Node* itemNode, + MailboxName* mailboxName) +{ + /* Clear the mailbox name. */ + *mailboxName = ""; + + /* Get the mailbox name, and check that it's valid. */ + if (!complain_if_variable_not_a_valid_item_name(itemNode)) + { + return false; + } + + /* All good, assign and return. */ + *mailboxName = itemNode->str; /* It's really that simple. */ + return true; +} + +/* Find the section node corresponding to the type of an item. Requires the + * typedSections to be populated. + * + * Returns true if a section could be found, and false otherwise (while writing + * to the error string). Arguments: + * + * - itemType: What kind of item we are dealing with (box, board, mailbox...). + * - type: Type string to search for. + * - sectionNode: Pointer to write the found section to, set to PNULL if no + node could be found. */ +bool HardwareFileReader::d3_get_section_from_type( + std::string itemType, std::string type, UIF::Node** sectionNode) +{ + *sectionNode = PNULL; + + /* Iterator to find typed sections. Note that this only refers to the inner + * map of typedSections. */ + std::map::iterator typedSectionIterator; + + /* Go hunting. */ + typedSectionIterator = typedSections[itemType].find(type); + if (typedSectionIterator == typedSections[itemType].end()) + { + errors.push_back(dformat("L%u: Type '%s' defined in the '%s' section " + "does not correspond to a section.", + record->pos, type.c_str(), + sectionName.c_str())); + return false; + } + + else + { + *sectionNode = typedSectionIterator->second; + return true; + } +} + +/* Load types from the default types section, and validate them. + * + * Returns true if all fields were valid and describe sections, and false + * otherwise. NB: if the [default_types] section does not exist, returns true + * and does not touch the input array. This function appends errors to + * d3_errors as it goes. Arguments: + * + * - globalDefaults: Pointer to a 3-length array of UIF::Nodes, which*/ +bool HardwareFileReader::d3_get_validate_default_types( + UIF::Node** globalDefaults) +{ + bool anyErrors = false; /* Innocent until proven guilty. */ + sectionName = "default_types"; + + /* Valid fields for the header section. All are optional. */ + std::vector validFields; + std::vector::iterator fieldIterator; + validFields.push_back("box_type"); + validFields.push_back("board_type"); + validFields.push_back("mailbox_type"); + + /* Holds fields we've already grabbed (for validation purposes). */ + std::map fieldsFound; + for (fieldIterator=validFields.begin(); fieldIterator!=validFields.end(); + fieldIterator++) + { + fieldsFound.insert(std::make_pair(*fieldIterator, false)); + } + + /* Staging vectors for holding value nodes and variable nodes. */ + std::vector valueNodes; + std::vector variableNodes; + + /* Firstly, is there a [default_types] section? */ + std::map::iterator untypedSectionsIterator; + untypedSectionsIterator = untypedSections.find(sectionName); + if (untypedSectionsIterator == untypedSections.end()) + { + /* If not, let's go. */ + return true; + } + + /* The section exists for control to reach here. Get the default types from + * the [default_types] section. */ + std::vector recordNodes; + GetRecd(untypedSectionsIterator->second, recordNodes); + + for (std::vector::iterator recordIterator=recordNodes.begin(); + recordIterator!=recordNodes.end(); recordIterator++) + { + record = *recordIterator; + + /* Get the value and variable nodes. */ + GetVari(record, variableNodes); + GetValu(record, valueNodes); + + /* Ignore this record if the record has not got a variable/value + * pair (i.e. if the line is empty, or is just a comment). */ + if (variableNodes.size() == 0 and valueNodes.size() == 0){continue;} + + /* Complaints */ + if (!(complain_if_variable_not_plus_prefixed(variableNodes[0]) and + complain_if_variable_name_invalid(variableNodes[0], + &validFields) and + complain_if_record_is_multivariable(&variableNodes) and + complain_if_record_is_multivalue(&valueNodes))) + { + anyErrors = true; + continue; + } + + /* Complain if duplicate. NB: We know the variable name is valid if + * control has reached here. */ + if (complain_if_variable_true_in_map(variableNodes[0], &fieldsFound)) + { + anyErrors = true; + continue; + } + variable = variableNodes[0]->str; + fieldsFound[variable] = true; + + /* Specific logic for each variable. */ + std::string itemType; + unsigned arrayIndex; + if (variable == "box_type") + { + itemType = "box"; + arrayIndex = box; + } + else if (variable == "board_type") + { + itemType = "board"; + arrayIndex = board; + } + else if (variable == "mailbox_type") + { + itemType = "mailbox"; + arrayIndex = mailbox; + } + + /* Figure out if there is a section corresponding to this type. If not, + * whine loudly. Either way, assign to globalDefaults. */ + if (!d3_get_section_from_type(itemType, valueNodes[0]->str, + &(globalDefaults[arrayIndex]))) + { + anyErrors = true; + } + } + + return !anyErrors; +} diff --git a/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Items.cpp b/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Items.cpp new file mode 100644 index 00000000..516199cf --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Items.cpp @@ -0,0 +1,1736 @@ +/* Defined behaviour for the dialect 3 functionality of methods that create + * and/or modify items in the engine. */ + +#include "HardwareFileReader.h" + +/* Create some cores and threads, and pile them all into a mailbox. + * + * Returns true if there were no validation problems, and false + * otherwise. Arguments: + * + * - mailbox: Pointer to the mailbox to populate. + * - quatity: Number of cores to create (the number of threads will be + determined from the UIF parse tree. */ +bool HardwareFileReader::d3_create_cores_and_threads_for_mailbox( + P_mailbox* mailbox, unsigned coreQuantity) +{ + bool anyErrors = false; /* Innocent until proven guilty. */ + + /* We'll be modifying the 'record', 'sectionName', and 'variable' + * members. To ensure they are preserved on exit, store the old values + * here, and reinstate them once finished. */ + UIF::Node* callingRecord = record; + std::string callingSectionName = sectionName; + std::string callingVariable = variable; + + sectionName = "core"; + + /* Create all of the cores, and add them to the mailbox. Don't validate the + * address component (but still catch if we go out of bounds). */ + P_core* tmpCore; + AddressComponent coreId; + for (coreId = 0; coreId < coreQuantity; coreId++) + { + tmpCore = new P_core(dformat( + "C%0*u", how_many_digits(coreQuantity), coreId)); + try + { + mailbox->contain(coreId, tmpCore); + } + catch (OrchestratorException &e) + { + errors.push_back(e.message.c_str()); + anyErrors = true; + } + } + + /* Valid fields for the core section. All fields are mandatory. */ + std::vector validFields; + std::vector::iterator fieldIterator; + validFields.push_back("core_thread_cost"); + validFields.push_back("data_memory"); + validFields.push_back("instruction_memory"); + validFields.push_back("thread_thread_cost"); + validFields.push_back("threads"); + + /* Holds fields we've already grabbed (for validation purposes). */ + std::map fieldsFound; + for (fieldIterator=validFields.begin(); fieldIterator!=validFields.end(); + fieldIterator++) + { + fieldsFound.insert(std::make_pair(*fieldIterator, false)); + } + + /* Temporary staging vectors for holding value nodes and variable + * nodes. */ + std::vector valueNodes; + std::vector variableNodes; + + /* For iterating through cores when binding and creating threads. */ + std::map::iterator coreIterator; + + /* Iterate through all record nodes in this section, and apply properties + * to all cores within. */ + std::vector recordNodes; + GetRecd(untypedSections[sectionName], recordNodes); + + for (std::vector::iterator recordIterator=recordNodes.begin(); + recordIterator!=recordNodes.end(); recordIterator++) + { + record = *recordIterator; + + /* Get the value and variable nodes. */ + GetVari(record, variableNodes); + GetValu(record, valueNodes); + + /* Ignore this record if the record has not got a variable/value + * pair (i.e. if the line is empty, or is just a comment). */ + if (variableNodes.size() == 0 and valueNodes.size() == 0){continue;} + + /* Complaints */ + if (!(complain_if_variable_not_plus_prefixed(variableNodes[0]) and + complain_if_variable_name_invalid(variableNodes[0], + &validFields) and + complain_if_record_is_multivariable(&variableNodes) and + complain_if_record_is_multivalue(&valueNodes))) + { + anyErrors = true; + continue; + } + + /* Complain if duplicate. NB: We know the variable name is valid if + * control has reached here. */ + if (complain_if_variable_true_in_map(variableNodes[0], &fieldsFound)) + { + anyErrors = true; + continue; + } + variable = variableNodes[0]->str; + fieldsFound[variable] = true; + + /* Specific logic for each variable. */ + if (variable == "core_thread_cost") + { + /* Complain if not a float. */ + if (!complain_if_value_not_floating(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Bind! */ + for (coreIterator=mailbox->P_corem.begin(); + coreIterator!=mailbox->P_corem.end(); coreIterator++) + { + coreIterator->second->costCoreThread = \ + str2unsigned(valueNodes[0]->str); + } + } + + else if (variable == "data_memory") + { + /* Complain if not natural */ + if (!complain_if_value_not_natural(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Bind! */ + for (coreIterator=mailbox->P_corem.begin(); + coreIterator!=mailbox->P_corem.end(); coreIterator++) + { + coreIterator->second->dataMemory = \ + str2unsigned(valueNodes[0]->str); + } + } + + else if (variable == "instruction_memory") + { + /* Complain if not natural */ + if (!complain_if_value_not_natural(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Bind! */ + for (coreIterator=mailbox->P_corem.begin(); + coreIterator!=mailbox->P_corem.end(); coreIterator++) + { + coreIterator->second->instructionMemory = \ + str2unsigned(valueNodes[0]->str); + } + } + + else if (variable == "thread_thread_cost") + { + /* Complain if not a float. */ + if (!complain_if_value_not_floating(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Bind! */ + for (coreIterator=mailbox->P_corem.begin(); + coreIterator!=mailbox->P_corem.end(); coreIterator++) + { + coreIterator->second->costThreadThread = \ + str2unsigned(valueNodes[0]->str); + } + } + + else if (variable == "threads") + { + /* Complain if not natural */ + if (!complain_if_value_not_natural(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Create that many threads on each core, without validating the + * address component (but still catch if we go out of bounds). */ + std::map::iterator coreIterator; + unsigned threadId; + P_thread* tmpThread; + unsigned threadQuantity = str2unsigned(valueNodes[0]->str); + for (coreIterator=mailbox->P_corem.begin(); + coreIterator!=mailbox->P_corem.end(); coreIterator++) + { + for (threadId=0; threadIdsecond->contain(threadId, tmpThread); + } + catch (OrchestratorException &e) + { + errors.push_back(e.message.c_str()); + anyErrors = true; + break; + } + } + } + } + } + + /* Ensure mandatory fields have been defined. */ + if (!complain_if_mandatory_field_not_defined(&validFields, &fieldsFound)) + { + anyErrors = true; + } + + /* Restore the old 'record', 'sectionName', and 'variable' members. */ + record = callingRecord; + sectionName = callingSectionName; + variable = callingVariable; + + return !anyErrors; +} + +/* Define the fields of a board from a typed section. + * + * Returns true if there were no validation problems, and false + * otherwise. Arguments: + * + * - box: Pointer to the box to populate. + * - sectionNode: The node that defines the properties of the board. */ +bool HardwareFileReader::d3_define_board_fields_from_section( + P_board* board, UIF::Node* sectionNode) +{ + bool anyErrors = false; /* Innocent until proven guilty. */ + + /* We'll be modifying the 'record', 'sectionName', and 'variable' + * members. To ensure they are preserved on exit, store the old values + * here, and reinstate them once finished. */ + UIF::Node* callingRecord = record; + std::string callingSectionName = sectionName; + std::string callingVariable = variable; + + /* Short on time, sorry... */ + sectionName = dformat( + "%s(%s)", sectionNode->leaf[0]->leaf[0]->str.c_str(), + sectionNode->leaf[0]->leaf[0]->leaf[0]->str.c_str()); + + /* Valid fields for board sections */ + std::vector validFields; + std::vector::iterator fieldIterator; + validFields.push_back("board_mailbox_cost"); + validFields.push_back("dram"); + validFields.push_back("mailbox_mailbox_cost"); + validFields.push_back("supervisor_memory"); + validFields.push_back("type"); + + /* Mandatory fields for board sections.. */ + std::vector mandatoryFields; + mandatoryFields.push_back("board_mailbox_cost"); + mandatoryFields.push_back("dram"); + mandatoryFields.push_back("mailbox_mailbox_cost"); + mandatoryFields.push_back("supervisor_memory"); + + /* Holds fields we've already grabbed (for validation purposes). */ + std::map fieldsFound; + for (fieldIterator=validFields.begin(); fieldIterator!=validFields.end(); + fieldIterator++) + { + fieldsFound.insert(std::make_pair(*fieldIterator, false)); + } + + /* Temporary staging vectors for holding value nodes and variable + * nodes. */ + std::vector valueNodes; + std::vector variableNodes; + + /* Iterate through all record nodes in this section. */ + std::vector recordNodes; + GetRecd(sectionNode, recordNodes); + + for (std::vector::iterator recordIterator=recordNodes.begin(); + recordIterator!=recordNodes.end(); recordIterator++) + { + record = *recordIterator; + + /* Get the value and variable nodes. */ + GetVari(record, variableNodes); + GetValu(record, valueNodes); + + /* Ignore this record if the record has not got a variable/value + * pair (i.e. if the line is empty, or is just a comment). */ + if (variableNodes.size() == 0 and valueNodes.size() == 0){continue;} + + /* Is this a variable definition? (does it have variables, values, and + * a '+' prefix)? If not, ignore it (for now). */ + if (valueNodes.size() == 0 or variableNodes.size() == 0 or + variableNodes[0]->qop != Lex::Sy_plus){continue;} + + /* Complaints for variable definitions. */ + if (!(complain_if_variable_name_invalid(variableNodes[0], + &validFields) and + complain_if_record_is_multivariable(&variableNodes) and + complain_if_record_is_multivalue(&valueNodes))) + { + anyErrors = true; + continue; + } + + /* Complain if duplicate. NB: We know the variable name is valid if + * control has reached here. */ + if (complain_if_variable_true_in_map(variableNodes[0], &fieldsFound)) + { + anyErrors = true; + continue; + } + variable = variableNodes[0]->str; + fieldsFound[variable] = true; + + /* Specific logic for each variable. */ + if (variable == "board_mailbox_cost") + { + /* Complain if not a float. */ + if (!complain_if_value_not_floating(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Bind */ + board->costBoardMailbox = str2float(valueNodes[0]->str); + } + + else if (variable == "dram") + { + /* Complain if not natural */ + if (!complain_if_value_not_natural(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Bind */ + board->dram = str2unsigned(valueNodes[0]->str); + } + + else if (variable == "mailbox_mailbox_cost") + { + /* Complain if not a float. */ + if (!complain_if_value_not_floating(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Hold for later use */ + defaultMailboxMailboxCost = str2float(valueNodes[0]->str); + isDefaultMailboxCostDefined = true; + } + + else if (variable == "supervisor_memory") + { + /* Complain if not natural */ + if (!complain_if_value_not_natural(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Bind */ + board->supervisorMemory = str2unsigned(valueNodes[0]->str); + } + + /* Types are processed in d3_get_validate_default_types, so we + * ignore the definition here. */ + else if (variable == "type") + { + + } + } + + /* Ensure mandatory fields have been defined. */ + if (!complain_if_mandatory_field_not_defined(&mandatoryFields, + &fieldsFound)) + { + anyErrors = true; + } + + /* Restore the old 'record', 'sectionName', and 'variable' members. */ + record = callingRecord; + sectionName = callingSectionName; + variable = callingVariable; + + return !anyErrors; +} + +/* Define the fields of a box from a typed section. + * + * Returns true if there were no validation problems, and false + * otherwise. Arguments: + * + * - box: Pointer to the box to populate. + * - sectionNode: The node that defines the properties of the box. */ +bool HardwareFileReader::d3_define_box_fields_from_section( + P_box* box, UIF::Node* sectionNode) +{ + bool anyErrors = false; /* Innocent until proven guilty. */ + + /* We'll be modifying the 'record', 'sectionName', and 'variable' + * members. To ensure they are preserved on exit, store the old values + * here, and reinstate them once finished. */ + UIF::Node* callingRecord = record; + std::string callingSectionName = sectionName; + std::string callingVariable = variable; + + /* Short on time, sorry...*/ + sectionName = dformat( + "%s(%s)", sectionNode->leaf[0]->leaf[0]->str.c_str(), + sectionNode->leaf[0]->leaf[0]->leaf[0]->str.c_str()); + + /* Valid fields for the box section (all are mandatory). */ + std::vector validFields; + std::vector::iterator fieldIterator; + validFields.push_back("box_board_cost"); + validFields.push_back("supervisor_memory"); + + /* Holds fields we've already grabbed (for validation purposes). */ + std::map fieldsFound; + for (fieldIterator=validFields.begin(); fieldIterator!=validFields.end(); + fieldIterator++) + { + fieldsFound.insert(std::make_pair(*fieldIterator, false)); + } + + /* Temporary staging vectors for holding value nodes and variable + * nodes. */ + std::vector valueNodes; + std::vector variableNodes; + + /* Iterate through all record nodes in this section. */ + std::vector recordNodes; + GetRecd(sectionNode, recordNodes); + + for (std::vector::iterator recordIterator=recordNodes.begin(); + recordIterator!=recordNodes.end(); recordIterator++) + { + record = *recordIterator; + + /* Get the value and variable nodes. */ + GetVari(record, variableNodes); + GetValu(record, valueNodes); + + /* Ignore this record if the record has not got a variable/value + * pair (i.e. if the line is empty, or is just a comment). */ + if (variableNodes.size() == 0 and valueNodes.size() == 0){continue;} + + /* Complaints */ + if (!(complain_if_variable_not_plus_prefixed(variableNodes[0]) and + complain_if_variable_name_invalid(variableNodes[0], + &validFields) and + complain_if_record_is_multivariable(&variableNodes) and + complain_if_record_is_multivalue(&valueNodes))) + { + anyErrors = true; + continue; + } + + /* Complain if duplicate. NB: We know the variable name is valid if + * control has reached here. */ + if (complain_if_variable_true_in_map(variableNodes[0], &fieldsFound)) + { + anyErrors = true; + continue; + } + variable = variableNodes[0]->str; + fieldsFound[variable] = true; + + /* Specific logic for each variable. */ + if (variable == "box_board_cost") + { + /* Complain if not a float. */ + if (!complain_if_value_not_floating(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Bind */ + box->costBoxBoard = str2float(valueNodes[0]->str); + } + + else if (variable == "supervisor_memory") + { + /* Complain if not natural */ + if (!complain_if_value_not_natural(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Bind */ + box->supervisorMemory = str2unsigned(valueNodes[0]->str); + } + } + + /* Ensure mandatory fields have been defined. */ + if (!complain_if_mandatory_field_not_defined(&validFields, &fieldsFound)) + { + anyErrors = true; + } + + /* Restore the old 'record', 'sectionName', and 'variable' members. */ + record = callingRecord; + sectionName = callingSectionName; + variable = callingVariable; + + return !anyErrors; +} + +/* Define the fields of a mailbox from a typed section, and add cores and + * threads. + * + * Returns true if there were no validation problems, and false + * otherwise. Arguments: + * + * - mailbox: Pointer to the mailbox to populate + * - sectionNode: The node that defines the properties of the mailbox. */ +bool HardwareFileReader::d3_define_mailbox_fields_from_section( + P_mailbox* mailbox, UIF::Node* sectionNode) +{ + bool anyErrors = false; /* Innocent until proven guilty. */ + + /* We'll be modifying the 'record', 'sectionName', and 'variable' + * members. To ensure they are preserved on exit, store the old values + * here, and reinstate them once finished. */ + UIF::Node* callingRecord = record; + std::string callingSectionName = sectionName; + std::string callingVariable = variable; + + /* Short on time, sorry... */ + sectionName = dformat( + "%s(%s)", sectionNode->leaf[0]->leaf[0]->str.c_str(), + sectionNode->leaf[0]->leaf[0]->leaf[0]->str.c_str()); + + /* Valid fields for mailbox sections (all are mandatory). */ + std::vector validFields; + std::vector::iterator fieldIterator; + validFields.push_back("core_core_cost"); + validFields.push_back("mailbox_core_cost"); + validFields.push_back("cores"); + + /* Holds fields we've already grabbed (for validation purposes). */ + std::map fieldsFound; + for (fieldIterator=validFields.begin(); fieldIterator!=validFields.end(); + fieldIterator++) + { + fieldsFound.insert(std::make_pair(*fieldIterator, false)); + } + + /* Temporary staging vectors for holding value nodes and variable + * nodes. */ + std::vector valueNodes; + std::vector variableNodes; + + /* Iterate through all record nodes in this section. */ + std::vector recordNodes; + GetRecd(sectionNode, recordNodes); + + for (std::vector::iterator recordIterator=recordNodes.begin(); + recordIterator!=recordNodes.end(); recordIterator++) + { + record = *recordIterator; + + /* Get the value and variable nodes. */ + GetVari(record, variableNodes); + GetValu(record, valueNodes); + + /* Ignore this record if the record has not got a variable/value + * pair (i.e. if the line is empty, or is just a comment). */ + if (variableNodes.size() == 0 and valueNodes.size() == 0){continue;} + + /* Complaints */ + if (!(complain_if_variable_not_plus_prefixed(variableNodes[0]) and + complain_if_variable_name_invalid(variableNodes[0], + &validFields) and + complain_if_record_is_multivariable(&variableNodes) and + complain_if_record_is_multivalue(&valueNodes))) + { + anyErrors = true; + continue; + } + + /* Complain if duplicate. NB: We know the variable name is valid if + * control has reached here. */ + if (complain_if_variable_true_in_map(variableNodes[0], &fieldsFound)) + { + anyErrors = true; + continue; + } + variable = variableNodes[0]->str; + fieldsFound[variable] = true; + + /* Specific logic for each variable. */ + if (variable == "core_core_cost") + { + /* Complain if not a float. */ + if (!complain_if_value_not_floating(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Bind */ + mailbox->costCoreCore = str2float(valueNodes[0]->str); + } + + else if (variable == "cores") + { + /* Complain if not natural */ + if (!complain_if_value_not_natural(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Create and add that many cores. */ + if (!d3_create_cores_and_threads_for_mailbox( + mailbox, str2unsigned(valueNodes[0]->str))) + { + anyErrors = true; + continue; + } + } + + else if (variable == "mailbox_core_cost") + { + /* Complain if not a float. */ + if (!complain_if_value_not_floating(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Bind */ + mailbox->costMailboxCore = str2float(valueNodes[0]->str); + } + } + + /* Ensure mandatory fields have been defined. */ + if (!complain_if_mandatory_field_not_defined(&validFields, &fieldsFound)) + { + anyErrors = true; + } + + /* Restore the old 'record', 'sectionName', and 'variable' members. */ + record = callingRecord; + sectionName = callingSectionName; + variable = callingVariable; + + return !anyErrors; +} + +/* Creates and 'contains' mailboxes for this board, as well as the items + * beneath. + * + * Relies on the default-typing structures being populated, as well as the + * default mailbox-mailbox cost (if there is one). Returns true if all + * validation checks pass, and false otherwise. Arguments: + * + * - board: Board to populate with mailboxes, which are in turn populated with + * cores etc. + * - sectionNode: The node that defines the mailboxes in boards of this + * type. */ +bool HardwareFileReader::d3_populate_validate_board_with_mailboxes( + P_board* board, UIF::Node* sectionNode) +{ + bool anyErrors = false; /* Innocent until proven guilty. */ + + /* We'll be modifying the 'record', 'sectionName', and 'variable' + * members. To ensure they are preserved on exit, store the old values + * here, and reinstate them once finished. */ + UIF::Node* callingRecord = record; + std::string callingSectionName = sectionName; + std::string callingVariable = variable; + + /* Short on time, sorry... */ + sectionName = dformat( + "%s(%s)", sectionNode->leaf[0]->leaf[0]->str.c_str(), + sectionNode->leaf[0]->leaf[0]->leaf[0]->str.c_str()); + + /* Temporary staging vectors for holding value nodes and variable + * nodes. */ + std::vector valueNodes; + std::vector variableNodes; + + /* Holds the type of the current record, and the section it refers to (if + * any). */ + std::string type; + UIF::Node* sectionType; + + /* Holds the concatenated address of a mailbox. */ + AddressComponent address; + + /* Holds the mailbox while we're creating it. */ + P_mailbox* mailbox; + MailboxName mailboxName; + + /* Holds mailboxes while connecting them together. */ + std::vector joinedMailboxes; + + /* Holds information on all mailboxes in the current board. The key is the + * name of the mailbox in this board, and the value holds the information + * about that mailbox. */ + std::map mailboxInfoFromName; + + /* For finding mailbox names in mailboxInfoFromName. */ + std::map::iterator mailboxNameFinder; + + /* Holds the edge that connects to mailboxes together in a given board, if + * any. The two elements of the pair held in the key of the map represent + * the mailboxes at each end of the edge, and the value of the map holds + * the mailbox information. */ + std::map, EdgeInfo> mailboxEdges; + + /* For finding a reverse edge, and for iterating through mailboxEdges. */ + std::map, EdgeInfo>::iterator \ + edgeFinder; + + /* Holds an edge-specific mailbox-mailbox cost, if found. */ + float thisEdgeCost; + bool isThisEdgeCostDefined; + + /* Holds the name of the mailbox on the other end of an edge. */ + MailboxName edgeMailboxName; + + + + /* Iterate through all record nodes in this section that define + * mailboxes. */ + std::vector recordNodes; + GetRecd(sectionNode, recordNodes); + + for (std::vector::iterator recordIterator=recordNodes.begin(); + recordIterator!=recordNodes.end(); recordIterator++) + { + record = *recordIterator; + sectionType = PNULL; + + /* Get the value and variable nodes. */ + GetVari(record, variableNodes); + GetValu(record, valueNodes); + + /* Ignore this record if the record has not got a variable/value + * pair (i.e. if the line is empty, or is just a comment). */ + if (variableNodes.size() == 0 and valueNodes.size() == 0){continue;} + + /* Is this a variable definition? (does it have variables, values, and + * a '+' prefix)? If so, ignore it (it's already been dealt with in + * d3_define_board_fields_from_section. */ + if (valueNodes.size() != 0 and variableNodes.size() != 0 and + variableNodes[0]->qop == Lex::Sy_plus){continue;} + + /* Validate and get the name of the mailbox. */ + if (!d3_get_mailbox_name(variableNodes[0], &mailboxName)) + { + anyErrors = true; + continue; + } + + /* Whine if a mailbox with this name already exists in this board. */ + mailboxNameFinder = mailboxInfoFromName.find(mailboxName); + if (mailboxNameFinder != mailboxInfoFromName.end()) + { + errors.push_back(dformat( + "L%u: Mailbox on this line has already been defined on line " + "%u. Not making it.", + record->pos, mailboxNameFinder->second.lineNumber)); + anyErrors = true; + continue; + } + + /* Is a type explicitly defined in this record? */ + if (d3_get_explicit_type_from_item_definition(variableNodes[0], &type)) + { + /* If there's a matching section for this type, we're all good (it + * gets written to sectionType). Otherwise, we fall back to + * defaults. */ + anyErrors |= !d3_get_section_from_type( + "mailbox", type, §ionType); + } + + /* If the type it was not defined explicitly, or it was and no section + * matched with it, get the section from the defaults. */ + if (sectionType == PNULL) + { + sectionType = defaultTypes[sectionNode]; + + /* If it's still zero, then the type hasn't been defined anywhere + * "validly". That's an error, folks. */ + if (sectionType == PNULL) + { + errors.push_back(dformat( + "L%u: No section found to define the mailbox on this " + "line. Not making it.", record->pos)); + anyErrors = true; + continue; /* Can't do anything without a type definition... */ + } + } + + /* Get the address without validating it (boss' orders). */ + d3_get_address_from_item_definition(variableNodes[0], &address); + + /* Create the mailbox (the name argument is the name of the mailbox in + * the record). */ + mailbox = new P_mailbox(mailboxName); + + /* Into the board with ye! */ + board->contain(address, mailbox); + + /* Track the mailbox by name. */ + mailboxInfoFromName[mailboxName] = + MailboxInfo{record->pos, mailbox}; + + /* Stage the edges from this record. Values (i.e. LHS of the '=' token) + * each represent the name of a mailbox, optionally with a cost, which + * defines an edge */ + for(std::vector::iterator edgeIterator=valueNodes.begin(); + edgeIterator!=valueNodes.end(); edgeIterator++) + { + /* Get the name of the mailbox on the other side of this edge, + * skipping if invalid. */ + if(!d3_get_mailbox_name(*edgeIterator, &edgeMailboxName)) + { + anyErrors = true; + continue; /* Skip this edge - can't identify either end. */ + } + + /* If the edge explicitly describes a cost, use that. Otherwise, + * use one we found earlier. Complain if: + * + * - neither are defined + * - the explicit cost is invalid (we checked the default cost + * earlier) */ + isThisEdgeCostDefined = d3_get_explicit_cost_from_edge_definition( + *edgeIterator, &thisEdgeCost); + if (isThisEdgeCostDefined) + { + /* Check for -1, meaning the cost was invalid. */ + if (thisEdgeCost == -1) + { + errors.push_back(dformat( + "L%u: Invalid cost on edge connecting mailbox %s to " + "mailbox %s (it must be a float).", record->pos, + mailboxName.c_str(), edgeMailboxName.c_str())); + anyErrors = true; + continue; /* Skip this edge - meaningless without a + * comprehensible cost. */ + } + } + else if (isDefaultMailboxCostDefined) + { + thisEdgeCost = defaultMailboxMailboxCost; + } + else + { + errors.push_back(dformat( + "L%u: No cost found for edge connecting mailbox %s to " + "mailbox %s (it must be a float).", record->pos, + mailboxName.c_str(), edgeMailboxName.c_str())); + anyErrors = true; + continue; /* Skip this edge - meaningless without a cost. */ + } + + /* If the reverse edge is in mailboxEdges... */ + /* Bears reiterating - it's the reverse! */ + edgeFinder = mailboxEdges.find(std::make_pair(edgeMailboxName, + mailboxName)); + if (edgeFinder != mailboxEdges.end()) + { + /* Complain if: + * + * - The reverse-cost is different. + * - Reverse is already defined. */ + if (edgeFinder->second.weight != thisEdgeCost) + { + errors.push_back(dformat( + "L%u: The cost of the edge connecting mailbox %s to " + "mailbox %s (%f) is different from the cost of its " + "reverse (%f), defined at L%i.", + record->pos, + mailboxName.c_str(), + edgeMailboxName.c_str(), + thisEdgeCost, + edgeFinder->second.weight, + edgeFinder->second.lineNumber)); + anyErrors = true; + continue; /* Skip this edge, avoid clobbering. */ + } + + if (edgeFinder->second.isReverseDefined) + { + errors.push_back(dformat( + "L%u: The other end of the edge connecting mailbox " + "%s to mailbox %s has already been defined. The first " + "definition was at L%i.", + record->pos, + mailboxName.c_str(), + edgeMailboxName.c_str(), + edgeFinder->second.lineNumber)); + anyErrors = true; + continue; /* Skip this edge, avoid clobbering. */ + } + + /* We're all good, mark the reverse as found. */ + edgeFinder->second.isReverseDefined = true; + } + + /* Otherwise, track this edge in mailboxEdges. */ + else + { + /* But complain if it's already there (means we've defined it + * twice on this line, probably). */ + + /* NB: Not reverse! */ + edgeFinder = mailboxEdges.find( + std::make_pair(mailboxName, edgeMailboxName)); + if (edgeFinder != mailboxEdges.end()) + { + errors.push_back(dformat( + "L%u: Duplicate edge definition connecting mailbox " + "%s to mailbox %s.", + record->pos, + mailboxName.c_str(), + edgeMailboxName.c_str())); + anyErrors = true; + continue; /* Skip this edge, avoid clobbering. */ + } + + /* Okay, actually add it now. */ + mailboxEdges[std::make_pair(mailboxName, edgeMailboxName)] = \ + EdgeInfo{thisEdgeCost, false, record->pos}; + } + } /* That's all the edges. */ + + /* Define properties of this mailbox, and add cores. */ + if (!d3_define_mailbox_fields_from_section(mailbox, sectionType)) + { + anyErrors = true; + } + } + + /* Connect mailboxes together */ + for (edgeFinder=mailboxEdges.begin(); edgeFinder!=mailboxEdges.end(); + edgeFinder++) + { + /* Get the mailboxes. Complain if one of the mailbox names does not + * exist. For each name in the pair, check it matches a record in + * mailboxInfoFromName. */ + joinedMailboxes.clear(); + MailboxName thisName = edgeFinder->first.first; + while (true) /* Loop over both names in the map key (LM1). A + * concession to the fact that one can't iterate over a + * std::pair. */ + { + mailboxNameFinder = mailboxInfoFromName.find(thisName); + if (mailboxNameFinder == mailboxInfoFromName.end()) + { + errors.push_back(dformat( + "L%u: Could not find a definition for mailbox %s defined " + "by an edge in this record.", + edgeFinder->second.lineNumber, thisName.c_str())); + { + anyErrors = true; + } + } + + else + { + joinedMailboxes.push_back( + mailboxNameFinder->second.memoryAddress); + } + + /* Increment iteration, or leave (LM1). */ + if (thisName != edgeFinder->first.second) + { + thisName = edgeFinder->first.second; + } + else{break;} + } + + /* Complain if the reverse of an edge has not been defined. */ + if (!(edgeFinder->second.isReverseDefined)) + { + errors.push_back(dformat( + "L%u: Could not find the reverse edge definition connecting " + "mailboxes %s and %s.", edgeFinder->second.lineNumber, + edgeFinder->first.first, edgeFinder->first.second)); + anyErrors = true; + continue; + } + + /* Hook them up. */ + board->connect( + joinedMailboxes[0]->get_hardware_address()->get_mailbox(), + joinedMailboxes[1]->get_hardware_address()->get_mailbox(), + edgeFinder->second.weight); + } + + /* Restore the old 'record', 'sectionName', and 'variable' members. */ + record = callingRecord; + sectionName = callingSectionName; + variable = callingVariable; + + return !anyErrors; +} + +/* Validate the contents of the engine_board section, and create boards and + * items beneath. Relies on engine_box having been read. + * + * Returns true if all validation checks pass, and false otherwise. Arguments: + * + * - engine: Engine to populate with boards, and other items. */ +bool HardwareFileReader::d3_populate_validate_engine_board_and_below( + P_engine* engine) +{ + bool anyErrors = false; /* Innocent until proven guilty. */ + sectionName = "engine_board"; + + /* Valid fields for this section (none are mandatory). */ + std::vector validFields; + std::vector::iterator fieldIterator; + validFields.push_back("board_board_cost"); + validFields.push_back("type"); + + /* Holds fields we've already grabbed (for validation purposes). */ + std::map fieldsFound; + for (fieldIterator=validFields.begin(); fieldIterator!=validFields.end(); + fieldIterator++) + { + fieldsFound.insert(std::make_pair(*fieldIterator, false)); + } + + /* Temporary staging vectors for holding value nodes and variable + * nodes. */ + std::vector valueNodes; + std::vector variableNodes; + + /* Holds the type of the current record, and the section it refers to (if + * any). */ + std::string type; + UIF::Node* sectionType; + + /* Holds the concatenated address of a board. */ + AddressComponent address; + + /* Holds the board while we're creating it. */ + P_board* board; + BoardName boardName; + + /* Holds boards while connecting them together. */ + std::vector joinedBoards; + + /* Holds information on all boards. The key is the unique name of the + * board, and the value holds the information about that board. */ + std::map boardInfoFromName; + + /* For finding board names in boardInfoFromName. */ + std::map::iterator boardNameFinder; + + /* Holds the edge that connects to boards together, if any. The two + * elements of the pair held in the key of the map represent the boards at + * each end of the edge, and the value of the map holds the edge + * information. */ + std::map, EdgeInfo> boardEdges; + + /* For finding a reverse edge, and for iterating through the edge + * container. */ + std::map, EdgeInfo>::iterator \ + edgeFinder; + + /* Holds any default board-board cost, if found. */ + float defaultCost = 0; + bool isDefaultCostDefined = false; + + /* Holds an edge-specific board-board cost, if found. */ + float thisEdgeCost; + bool isThisEdgeCostDefined; + + /* Holds the name of the board on the other end of an edge. */ + BoardName edgeBoardName; + + /* Iterate through all record nodes in this section, in order to: + * + * - Get the default board-board cost. + * - Complain if there are duplicate entries. + * + * Ignore records that are not variable definitions. */ + std::vector recordNodes; + GetRecd(untypedSections[sectionName], recordNodes); + + std::vector::iterator recordIterator; + for (recordIterator=recordNodes.begin(); + recordIterator!=recordNodes.end(); recordIterator++) + { + record = *recordIterator; + + /* Get the value and variable nodes. */ + GetVari(record, variableNodes); + GetValu(record, valueNodes); + + /* Ignore this record if the record has not got a variable/value + * pair (i.e. if the line is empty, or is just a comment). */ + if (variableNodes.size() == 0 and valueNodes.size() == 0){continue;} + + /* Only proceed if this record is a variable definition (it must have a + * '+' prefix) */ + if (variableNodes[0]->qop != Lex::Sy_plus){continue;} + + /* Complaints for variable definitions. */ + if (!(complain_if_variable_name_invalid(variableNodes[0], + &validFields) and + complain_if_record_is_multivariable(&variableNodes) and + complain_if_record_is_multivalue(&valueNodes))) + { + anyErrors = true; + continue; + } + + /* Complain if duplicate. NB: We know the variable name is valid if + * control has reached here. */ + if (complain_if_variable_true_in_map(variableNodes[0], &fieldsFound)) + { + anyErrors = true; + continue; + } + variable = variableNodes[0]->str; + fieldsFound[variable] = true; + + /* Specific logic for each variable. */ + if (variable == "board_board_cost") + { + /* Complain if not a float. */ + if (!complain_if_value_not_floating(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Hold him! */ + defaultCost = str2unsigned(valueNodes[0]->str); + isDefaultCostDefined = true; + } + + /* Types are processed in d3_get_validate_default_types, so we + * ignore the definition here. */ + else if (variable == "type") + { + + } + } + + /* Iterate through all record nodes in this section, in order to define + * boards. We ignore variable definitions this time. Yeah we're iterating + * twice... */ + for (recordIterator=recordNodes.begin(); + recordIterator!=recordNodes.end(); recordIterator++) + { + record = *recordIterator; + sectionType = PNULL; /* Given that this is a board record, we have to + * find a section that defines the properties of + * this board. */ + + /* Get the value and variable nodes. */ + GetVari(record, variableNodes); + GetValu(record, valueNodes); + + /* Ignore this record if the record has not got a variable/value + * pair (i.e. if the line is empty, or is just a comment). */ + if (variableNodes.size() == 0 and valueNodes.size() == 0){continue;} + + /* Only proceed if this record is NOT a variable definition (it must + * NOT have a '+' prefix) */ + if (variableNodes[0]->qop == Lex::Sy_plus){continue;} + + /* Validate and get the name of the board. */ + if (!d3_get_board_name(variableNodes[0], &boardName)) + { + anyErrors = true; + continue; + } + + /* Whine if a board with this name already exists. */ + std::map::iterator boardNameFinder; + boardNameFinder = boardInfoFromName.find(boardName); + if (boardNameFinder != boardInfoFromName.end()) + { + errors.push_back(dformat( + "L%u: Board name on this line has already been defined on " + "line %u. Not making it.", + record->pos, boardNameFinder->second.lineNumber)); + anyErrors = true; + continue; + } + + /* Is a type explicitly defined in this record? */ + if (d3_get_explicit_type_from_item_definition(variableNodes[0], &type)) + { + /* If there's a matching section for this type, we're all good (it + * gets written to sectionType). Otherwise, we fall back to + * defaults. */ + if (!d3_get_section_from_type("board", type, §ionType)) + { + anyErrors = true; + } + } + + /* If the type it was not defined explicitly, or it was and no section + * matched with it, get the section from the defaults. */ + if (sectionType == PNULL) + { + sectionType = defaultTypes[untypedSections[sectionName]]; + + /* If it's still zero, then the type hasn't been defined anywhere + * "validly". That's an error, folks. */ + if (sectionType == PNULL) + { + errors.push_back(dformat( + "L%u: No section found to define the board on this line. " + "Not making it.", record->pos)); + anyErrors = true; + continue; /* Can't do anything without a type definition... */ + } + } + + /* Get the address without validating it (boss' orders). */ + d3_get_address_from_item_definition(variableNodes[0], &address); + + /* Remove the board from undefinedBoards (it was put there during box + * declaration, but now we know it exists). */ + undefinedBoards.remove(boardName); + + /* Create the board (the name argument is the name of the board in the + * record). */ + board = new P_board(boardName.second); + + /* Into the engine with ye! */ + boxFromName[boardName.first]->contain(address, board); + engine->contain(address, board); + + /* Track the board by name. */ + boardInfoFromName[boardName] = + BoardInfo{record->pos, board}; + + /* Stage the edges from this record. Values (i.e. LHS of the '=' token) + * each represent the name of a board, optionally with a cost, which + * defines an edge */ + for(std::vector::iterator edgeIterator=valueNodes.begin(); + edgeIterator!=valueNodes.end(); edgeIterator++) + { + /* Get the name of the board on the other side of this edge, + * skipping if invalid. */ + if(!d3_get_board_name(*edgeIterator, &edgeBoardName)) + { + anyErrors = true; + continue; /* Skip this edge - can't identify either end. */ + } + + /* If the edge explicitly describes a cost, use that. Otherwise, + * use one we found earlier. Complain if: + * + * - neither are defined + * - the explicit cost is invalid (we checked the default cost + * earlier) */ + isThisEdgeCostDefined = d3_get_explicit_cost_from_edge_definition( + *edgeIterator, &thisEdgeCost); + if (isThisEdgeCostDefined) + { + /* Check for -1, meaning the cost was invalid. */ + if (thisEdgeCost == -1) + { + errors.push_back(dformat( + "L%u: Invalid cost on edge connecting board " + "%s(board(%s)) to board %s(board(%s)) (it must be a " + "float).", + record->pos, + boardName.first.c_str(), + boardName.second.c_str(), + edgeBoardName.first.c_str(), + edgeBoardName.second.c_str())); + anyErrors = true; + continue; /* Skip this edge - meaningless without a + * comprehensible cost. */ + } + } + else if (isDefaultCostDefined) + { + thisEdgeCost = defaultCost; + } + else + { + errors.push_back(dformat( + "L%u: No cost found for edge connecting board " + "%s(board(%s)) to board %s(board(%s)) (it must be a " + "float).", + record->pos, + boardName.first.c_str(), + boardName.second.c_str(), + edgeBoardName.first.c_str(), + edgeBoardName.second.c_str())); + anyErrors = true; + continue; /* Skip this edge - meaningless without a cost. */ + } + + /* If the reverse edge is in boardEdges... */ + /* Bears reiterating - it's the reverse! */ + edgeFinder = boardEdges.find(std::make_pair(edgeBoardName, + boardName)); + if (edgeFinder != boardEdges.end()) + { + /* Complain if: + * + * - The reverse-cost is different. + * - Reverse is already defined. */ + if (edgeFinder->second.weight != thisEdgeCost) + { + errors.push_back(dformat( + "L%u: The cost of the edge connecting board " + "%s(board(%s)) to board %s(board(%s)) (%f) is " + "different from the cost of its reverse (%f), defined " + "at L%i.", + record->pos, + boardName.first.c_str(), + boardName.second.c_str(), + edgeBoardName.first.c_str(), + edgeBoardName.second.c_str(), + thisEdgeCost, + edgeFinder->second.weight, + edgeFinder->second.lineNumber)); + anyErrors = true; + continue; /* Skip this edge, avoid clobbering. */ + } + + if (edgeFinder->second.isReverseDefined) + { + errors.push_back(dformat( + "L%u: The other end of the edge connecting board " + "%s(board(%s)) to board %s(board(%s)) has already " + "been defined. The first definition was at L%i.", + record->pos, + boardName.first.c_str(), + boardName.second.c_str(), + edgeBoardName.first.c_str(), + edgeBoardName.second.c_str(), + edgeFinder->second.lineNumber)); + anyErrors = true; + continue; /* Skip this edge, avoid clobbering. */ + } + + /* We're all good, mark the reverse as found (we're on it!). */ + edgeFinder->second.isReverseDefined = true; + } + + /* Otherwise, track this edge in boardEdges. */ + else + { + /* But complain if it's already there (means we've defined it + * twice on this line, probably). */ + + /* NB: Not reverse! */ + edgeFinder = boardEdges.find(std::make_pair(boardName, + edgeBoardName)); + if (edgeFinder != boardEdges.end()) + { + errors.push_back(dformat( + "L%u: Duplicate edge definition connecting board " + "%s(board(%s)) to board %s(board(%s)).", + record->pos, + boardName.first.c_str(), + boardName.second.c_str(), + edgeBoardName.first.c_str(), + edgeBoardName.second.c_str())); + anyErrors = true; + continue; /* Skip this edge, avoid clobbering. */ + } + + /* Okay, actually add it now. */ + boardEdges[std::make_pair(boardName, edgeBoardName)] = \ + EdgeInfo{thisEdgeCost, false, record->pos}; + } + } /* That's all the edges. */ + + /* Define the properties of this board. If any are missing or broken, + * continue (we fail slowly). */ + if (!d3_define_board_fields_from_section(board, sectionType)) + { + anyErrors = true; + } + + /* Populate the board with mailboxes. */ + if (!d3_populate_validate_board_with_mailboxes(board, sectionType)) + { + anyErrors = true; + } + } + + /* Connect boards together. */ + for (edgeFinder = boardEdges.begin(); edgeFinder != boardEdges.end(); + edgeFinder++) + { + /* Get the boards. Complain if one of the board names does not + * exist. For each name in the pair, check it matches a record in + * boardInfoFromName. */ + joinedBoards.clear(); + BoardName thisName = edgeFinder->first.first; + while (true) /* Loop over both names in the map key (LM1). A + * concession to the fact that one can't iterate over a + * std::pair. */ + { + boardNameFinder = boardInfoFromName.find(thisName); + if (boardNameFinder == boardInfoFromName.end()) + { + errors.push_back(dformat( + "L%u: Could not find a definition for board %s(board(%s)) " + "defined by an edge in this record.", + edgeFinder->second.lineNumber, + thisName.first.c_str(), thisName.second.c_str())); + { + anyErrors = true; + } + } + + else + { + joinedBoards.push_back(boardNameFinder->second.memoryAddress); + } + + /* Increment iteration, or leave (LM1). */ + if (thisName != edgeFinder->first.second) + { + thisName = edgeFinder->first.second; + } + else{break;} + } + + /* Complain if the reverse of an edge has not been defined. */ + if (!(edgeFinder->second.isReverseDefined)) + { + /* This is a bit obtuse, but that's pairs for you. Basically: + * + * - The first tier is a std::pair + * - The second tier identifies the board by name, and is a + * BoardName, which is a std::pair. + * - The third tier identifies either the box or board component of + * the board name, and is a std::string. */ + errors.push_back(dformat( + "L%u: Could not find the reverse edge definition connecting " + "boards %s(board(%s)) and %s(board(%s)).", + edgeFinder->second.lineNumber, + /* Box component of first board name. */ + edgeFinder->first.first.first.c_str(), + /* Board component of first board name. */ + edgeFinder->first.first.second.c_str(), + /* Box component of second board name. */ + edgeFinder->first.second.first.c_str(), + /* Board component of second board name. */ + edgeFinder->first.second.second.c_str())); + anyErrors = true; + continue; + } + + /* Hook them up. */ + engine->connect( + joinedBoards[0]->get_hardware_address()->get_board(), + joinedBoards[1]->get_hardware_address()->get_board(), + edgeFinder->second.weight); + } + + /* Check for boards that were defined as part of box declarations that have + * not beend defined. Generally, catch this problem: + * + * [engine_box] + * myBox(boards(B00,B01)) + * ... // Truncation + * [engine_board] + * myBox(board(B00),...)=... + * ... + * EOF + * // But I've not defined myBox(board(B01))! + * + * Also, we don't enter the loop if we've done nothing wrong. */ + std::list::iterator badBoardIterator; + for (badBoardIterator = undefinedBoards.begin(); + badBoardIterator != undefinedBoards.end(); badBoardIterator++) + { + errors.push_back(dformat( + "Board %s(board(%s)) has been declared in the 'engine_box' " + "section, but not defined in the 'engine_board' section.", + (*badBoardIterator).first.c_str(), + (*badBoardIterator).second.c_str())); + anyErrors = true; + } + + return !anyErrors; +} + +/* Validate the contents of the engine_box section, and populate an engine with + * them. Also creates boxes. + * + * Returns true if all validation checks pass, and false otherwise. Arguments: + * + * - engine: Engine to populate. */ +bool HardwareFileReader::d3_populate_validate_engine_box(P_engine* engine) +{ + bool anyErrors = false; /* Innocent until proven guilty. */ + sectionName = "engine_box"; + + /* Valid fields for this section. */ + std::vector validFields; + std::vector::iterator fieldIterator; + validFields.push_back("external_box_cost"); + validFields.push_back("type"); + + /* Holds fields we've already grabbed (for validation purposes). */ + std::map fieldsFound; + for (fieldIterator=validFields.begin(); fieldIterator!=validFields.end(); + fieldIterator++) + { + fieldsFound.insert(std::make_pair(*fieldIterator, false)); + } + + /* Temporary staging vectors for holding value nodes and variable + * nodes. */ + std::vector valueNodes; + std::vector variableNodes; + + /* Holds the type of the current record, and the section it refers to (if + * any). */ + std::string type; + UIF::Node* sectionType; + + /* Holds the concatenated address of a box. */ + AddressComponent address; + + /* Holds the box while we're creating it. */ + P_box* box; + std::string boxName; + + /* Iterate through all record nodes in this section. */ + std::vector recordNodes; + GetRecd(untypedSections[sectionName], recordNodes); + + for (std::vector::iterator recordIterator =recordNodes.begin(); + recordIterator!=recordNodes.end(); recordIterator++) + { + sectionName = "engine_box"; /* Will be changed by + * d3_define_box_fields_from_section, so + * we need to change it back in our + * loop. */ + record = *recordIterator; + sectionType = PNULL; /* Given that this is a box record, we have to + * find a section that defines the properties of + * this box. */ + + /* Get the value and variable nodes. */ + GetVari(record, variableNodes); + GetValu(record, valueNodes); + + /* Ignore this record if the record has not got a variable/value + * pair (i.e. if the line is empty, or is just a comment). */ + if (variableNodes.size() == 0 and valueNodes.size() == 0){continue;} + + /* Is this a variable definition? (does it have variables, values, and + * a '+' prefix)? (otherwise, we assume it is a box definition). */ + if (valueNodes.size() > 0 and variableNodes.size() > 0 and + variableNodes[0]->qop == Lex::Sy_plus) + { + /* Complaints for variable definitions. */ + if (!(complain_if_variable_name_invalid(variableNodes[0], + &validFields) and + complain_if_record_is_multivariable(&variableNodes) and + complain_if_record_is_multivalue(&valueNodes))) + { + anyErrors = true; + continue; + } + + + /* Complain if duplicate. NB: We know the variable name is valid if + * control has reached here. */ + if (complain_if_variable_true_in_map(variableNodes[0], + &fieldsFound)) + { + anyErrors = true; + continue; + } + variable = variableNodes[0]->str; + fieldsFound[variable] = true; + + /* Specific logic for each variable. */ + if (variable == "external_box_cost") + { + /* Complain if not a float. */ + if (!complain_if_value_not_floating(valueNodes[0])) + { + anyErrors = true; + continue; + } + + /* Bind! */ + engine->costExternalBox = str2unsigned(valueNodes[0]->str); + } + + /* Types are processed in d3_get_validate_default_types, so we + * ignore the definition here. */ + else if (variable == "type") + { + + } + + /* This continue is here to reduce the amount of indentation for + * dealing with box records. */ + continue; + } + + /* Otherwise, this must be a box definition. */ + + /* Complain if the name is invalid (but keep going). */ + if (!complain_if_variable_not_a_valid_item_name(variableNodes[0])) + { + anyErrors = true; + } + + /* Complain if a box already exists with this name (this is + * near-fatal). */ + std::map::iterator boxNameFinder; + boxName = variableNodes[0]->str; + boxNameFinder = boxFromName.find(boxName); + if(boxNameFinder != boxFromName.end()) + { + errors.push_back(dformat("L%u: Box name '%s' already defined.", + record->pos, boxName.c_str())); + anyErrors = true; + continue; + } + + /* Is a type explicitly defined in this record? */ + if (d3_get_explicit_type_from_item_definition(variableNodes[0], &type)) + { + /* If there's a matching section for this type, we're all good (it + * gets written to sectionType). Otherwise, we fall back to + * defaults. */ + if (!d3_get_section_from_type("box", type, §ionType)) + { + anyErrors = true; + } + } + + /* If the type it was not defined explicitly, or it was and no section + * matched with it, get the section from the defaults. */ + if (sectionType == PNULL) + { + sectionType = defaultTypes[untypedSections[sectionName]]; + + /* If it's still zero, then the type hasn't been defined anywhere + * "validly". That's an error, folks. */ + if (sectionType == PNULL) + { + errors.push_back(dformat( + "L%u: No section found to define the box on this line. " + "Not making it.", record->pos)); + anyErrors = true; + continue; /* Can't do anything without a type definition... */ + } + } + + /* Get the address without validating it (boss' orders). */ + d3_get_address_from_item_definition(variableNodes[0], &address); + + /* So we know we're dealing with a box definition, and we've got a + * type and an address for it. */ + + /* Create the box (the name argument is the name of the box in the + * record). */ + box = new P_box(boxName); + + /* Store conveniently. */ + boxFromName[boxName] = box; + + /* Into the engine with ye! */ + engine->contain(address, box); + + /* For each board in the box definition line, add it to + * undefinedBoards. */ + std::vector::iterator leafIterator; + leafIterator = variableNodes[0]->leaf.begin(); + while (leafIterator!=variableNodes[0]->leaf.end()) + { + if ((*leafIterator)->str == "boards"){break;} + leafIterator++; + } + + /* Don't panic if there aren't any boards to add. */ + if (leafIterator != variableNodes[0]->leaf.end()) + { + /* For each board... */ + std::vector::iterator lLeafIterator; /* Sorry */ + for (lLeafIterator=(*leafIterator)->leaf.begin(); + lLeafIterator!=(*leafIterator)->leaf.end(); lLeafIterator++) + { + /* Add to undefinedBoards. */ + undefinedBoards.push_back(BoardName(boxName, + (*lLeafIterator)->str)); + } + } + + /* Populate the fields of the box from its type definition. */ + anyErrors |= !d3_define_box_fields_from_section(box, sectionType); + + } /* End for-each-record */ + + return !anyErrors; +} + +/* Returns the number of digits in the argument. */ +int how_many_digits(unsigned printable){return (int)log10(printable) + 1;} diff --git a/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Sections.cpp b/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Sections.cpp new file mode 100644 index 00000000..19fcae34 --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/HardwareFileReaderDialect3Sections.cpp @@ -0,0 +1,608 @@ +/* Defines behaviour for the dialect 3 functionality of methods that preloads + * information about sections and types. */ + +#include "HardwareFileReader.h" + +/* Validate that the section occurences in the hardware description input file + * is semantically-valid in dialect 3. + * + * The following sections must be defined exactly once (0): + * + * - header + * - packet_address_format + * - engine_box + * - engine_board + * - core + * + * The following sections must be defined once or more (1): + * + * - box + * - board + * - mailbox + * + * The following sections must be defined once or less (2): + * + * - default_types + * + * The following sections must have arguments (and there must not be any + * duplicate arguments within a section type) (3): + * + * - box + * - board + * - mailbox + * + * The following sections must not have arguments (4): + * + * - packet_address_format + * - engine_box + * - engine_board + * - core + * - default_types + * + * No sections with names that are not covered by the above rules can exist + * (5). + * + * Returns true if the conditions above are all true, and false + * otherwise. */ +bool HardwareFileReader::d3_load_validate_sections() +{ + /* Valid section names */ + std::vector validSectionNames; + std::vector::iterator nameIterator; + validSectionNames.push_back("header"); + validSectionNames.push_back("packet_address_format"); + validSectionNames.push_back("engine_box"); + validSectionNames.push_back("engine_board"); + validSectionNames.push_back("core"); + validSectionNames.push_back("box"); + validSectionNames.push_back("board"); + validSectionNames.push_back("mailbox"); + validSectionNames.push_back("default_types"); + + /* Valid untyped section names (will use nameIterator to go through + * them). */ + std::vector validUntypedSectionNames; + validUntypedSectionNames.push_back("header"); + validUntypedSectionNames.push_back("packet_address_format"); + validUntypedSectionNames.push_back("engine_box"); + validUntypedSectionNames.push_back("engine_board"); + validUntypedSectionNames.push_back("core"); + validUntypedSectionNames.push_back("default_types"); + + /* Valid typed section names (will use nameIterator to go through them). */ + std::vector validTypedSectionNames; + validTypedSectionNames.push_back("box"); + validTypedSectionNames.push_back("board"); + validTypedSectionNames.push_back("mailbox"); + + /* Section names by rule. */ + std::vector> rules(5, std::vector()); + rules[0].push_back("header"); + rules[0].push_back("packet_address_format"); + rules[0].push_back("engine_box"); + rules[0].push_back("engine_board"); + rules[0].push_back("core"); + + rules[1].push_back("box"); + rules[1].push_back("board"); + rules[1].push_back("mailbox"); + + rules[2].push_back("default_types"); + + rules[3].push_back("box"); + rules[3].push_back("board"); + rules[3].push_back("mailbox"); + + rules[4].push_back("packet_address_format"); + rules[4].push_back("engine_box"); + rules[4].push_back("engine_board"); + rules[4].push_back("core"); + rules[4].push_back("default_types"); + unsigned currentRule = 0; + + /* Booleans to help with printing. */ + std::vector ruleFailure(6, false); + + /* Holding error messages constructed in stages. */ + std::string ruleErrors; + + /* Section name nodes, by name. */ + std::map> sectionsByName; + std::map>::iterator \ + sectionsByNameIterator; + for (nameIterator=validSectionNames.begin(); + nameIterator!=validSectionNames.end(); nameIterator++) + { + /* Map the string to an empty vector. */ + sectionsByName.insert(std::make_pair(*nameIterator, + std::vector())); + } + + /* For each of the section names found in the file, if it's a valid section + * name, add it to sectionsByName. If it's not, whine loudly and fail + * slow. */ + std::vector allNodes; + GetNames(allNodes); + + ruleErrors.clear(); + + std::vector::iterator nodeIterator; + for (nodeIterator=allNodes.begin(); nodeIterator!=allNodes.end(); + nodeIterator++) + { + /* Does this section node match one of the valid patterns? If so, add + * it to the appropriate vector in sectionsByName. */ + sectionsByNameIterator = sectionsByName.find((*nodeIterator)->str); + if (sectionsByNameIterator != sectionsByName.end()) + { + sectionsByNameIterator->second.push_back(*nodeIterator); + } + + /* Otherwise, it must be invalid (5). */ + else + { + /* On the first pass, add this header to the message. */ + if (!ruleFailure[5]) + { + ruleFailure[5] = true; + ruleErrors = "Sections with invalid names found:"; + } + + else + { + ruleErrors.append(","); + } + + ruleErrors.append(dformat(" %s (L%i)", + (*nodeIterator)->str.c_str(), + (*nodeIterator)->pos)); + } + } + + /* Add any errors found for this rule to the error container. */ + if (ruleFailure[5]) + { + errors.push_back(ruleErrors.c_str()); + } + + /* Rule (0): For each of the rules[0] strings... */ + ruleErrors.clear(); + for (nameIterator=rules[currentRule].begin(); + nameIterator!=rules[currentRule].end(); nameIterator++) + { + if (sectionsByName[*nameIterator].size() != 1) + { + /* On the first pass, add this header to the message. */ + if (!ruleFailure[currentRule]) + { + ruleFailure[currentRule] = true; + ruleErrors = "The following sections were not defined exactly " + "once:"; + } + + else + { + ruleErrors.append(","); + } + + /* I'm going to write the line numbers of each node, or "not + * defined" if the section is not defined. A little unusual because + * I want the printing to be pretty. */ + ruleErrors.append(dformat(" %s (", nameIterator->c_str())); + + if (sectionsByName[*nameIterator].empty()) + { + errors.push_back("not defined"); + } + + else + { + nodeIterator=sectionsByName[*nameIterator].begin(); + errors.push_back(dformat("L%u", (*nodeIterator)->pos)); + for (; + nodeIterator!=sectionsByName[*nameIterator].end(); + nodeIterator++) + { + errors.push_back(dformat(", L%u", (*nodeIterator)->pos)); + } + } + + errors.push_back(")"); + } + } + + /* Add any errors found for this rule to the error container. */ + if (ruleFailure[currentRule]) + { + errors.push_back(ruleErrors.c_str()); + } + + /* Rule (1) */ + currentRule++; + ruleErrors.clear(); + for (nameIterator=rules[currentRule].begin(); + nameIterator!=rules[currentRule].end(); nameIterator++) + { + if (sectionsByName[*nameIterator].empty()) + { + /* On the first pass, add this header to the message. */ + if (!ruleFailure[currentRule]) + { + ruleFailure[currentRule] = true; + ruleErrors = "The following sections were not defined when " + "they should have been defined one time or more:"; + } + + else + { + ruleErrors.append(","); + } + + ruleErrors.append(dformat(" %s", nameIterator->c_str())); + } + } + + /* Add any errors found for this rule to the error container. */ + if (ruleFailure[currentRule]) + { + errors.push_back(ruleErrors.c_str()); + } + + /* Rule (2) */ + currentRule++; + ruleErrors.clear(); + for (nameIterator=rules[currentRule].begin(); + nameIterator!=rules[currentRule].end(); nameIterator++) + { + if (sectionsByName[*nameIterator].size() >= 2) + { + /* On the first pass, add this header to the message. */ + if (!ruleFailure[currentRule]) + { + ruleFailure[currentRule] = true; + ruleErrors = "The following sections were defined more than " + "once, when they should have been defined one or " + "zero times:"; + } + + else + { + ruleErrors.append(","); + } + + /* I'm going to write the line numbers of each node. */ + errors.push_back(dformat(" %s (", nameIterator->c_str())); + nodeIterator=sectionsByName[*nameIterator].begin(); + errors.push_back(dformat("L%u", (*nodeIterator)->pos)); + for (; + nodeIterator!=sectionsByName[*nameIterator].end(); + nodeIterator++) + { + errors.push_back(dformat(", L%u", (*nodeIterator)->pos)); + } + errors.push_back(")\n"); + } + } + + /* Add any errors found for this rule to the error container. */ + if (ruleFailure[currentRule]) + { + errors.push_back(ruleErrors.c_str()); + } + + /* Rule (3) */ + std::vector declaredTypes; + std::vector arguments; + std::string argument; + currentRule++; + for (nameIterator=rules[currentRule].begin(); + nameIterator!=rules[currentRule].end(); nameIterator++) + { + declaredTypes.clear(); + for (nodeIterator=sectionsByName[*nameIterator].begin(); + nodeIterator!=sectionsByName[*nameIterator].end(); nodeIterator++) + { + /* Get arguments */ + arguments.clear(); + GetSub(*nodeIterator, arguments); + + /* If argument is empty, or there is no argument, or argument + * doesn't satisfy [0-9A-Za-z]{2,32}, then add to error message. */ + if (arguments.empty()) + { + ruleFailure[currentRule] = true; + errors.push_back(dformat( + "L%u: Section '%s' has no associated type.", + (*nodeIterator)->pos, (*nameIterator).c_str())); + } + + else if (arguments.size() > 1) + { + ruleFailure[currentRule] = true; + errors.push_back(dformat( + "L%u: Section '%s' has more than one type associated with " + "it.", (*nodeIterator)->pos, (*nameIterator).c_str())); + } + + else if (!is_type_valid(arguments[0])) + { + ruleFailure[currentRule] = true; + errors.push_back(dformat( + "L%u: Type '%s' in section '%s' is not a valid type (it " + "must satisfy %s).", (*nodeIterator)->pos, + arguments[0]->str.c_str(), (*nameIterator).c_str(), + TYPE_REGEX)); + } + + /* If we've already seen this type defined for a section of this + * sort (e.g. 'board' or 'mailbox'), then add to error message. */ + else if (std::find(declaredTypes.begin(), declaredTypes.end(), + arguments[0]->str) != declaredTypes.end()) + { + ruleFailure[currentRule] = true; + errors.push_back(dformat( + "L%u: Duplicate definition of section '%s' with type " + "'%s'.", (*nodeIterator)->pos, (*nameIterator).c_str(), + arguments[0]->str)); + } + + else + { + declaredTypes.push_back(arguments[0]->str); + } + } + } + + /* Rule (4) */ + currentRule++; + for (nameIterator=rules[currentRule].begin(); + nameIterator!=rules[currentRule].end(); nameIterator++) + { + for (nodeIterator=sectionsByName[*nameIterator].begin(); + nodeIterator!=sectionsByName[*nameIterator].end(); nodeIterator++) + { + /* Get arguments */ + arguments.clear(); + GetSub(*nodeIterator, arguments); + + /* If there is an argument, then add to error message. */ + if (!arguments.empty()) + { + ruleFailure[currentRule] = true; + errors.push_back(dformat( + "L%u: Section '%s' has a type, when it should not.", + (*nodeIterator)->pos, *nameIterator)); + } + } + } + + /* If none of the rules were broken, populate untypedSections and + * typedSections to facilitate better lookups later. + * + * We've been dealing only with section name nodes so far, but we need to + * store section nodes, so there's a bit of FndSect action here. + * + * We populate these structures after validating because the logic becomes + * overly complicated if section nodes do not have the correct number of + * name arguments. Once we're here, we can be confident that each section + * that has arguments has the appropriate number of them. It's not too + * expensive to do a second pass over the sections, because there are few + * of them. */ + UIF::Node* sectionNode; + if (std::find(ruleFailure.begin(), ruleFailure.end(), true) == \ + ruleFailure.end()) + { + /* Deal with the untyped sections first. */ + for (nameIterator=validUntypedSectionNames.begin(); + nameIterator!=validUntypedSectionNames.end(); nameIterator++) + { + /* Don't worry if there is no section name corresponding to this + * expected name; untyped sections may exist once or zero times + * (depending on the name of the section we're dealing with). */ + if (!sectionsByName[*nameIterator].empty()) + { + /* Get the section node from the section name node. */ + sectionNode = FndSect(sectionsByName[*nameIterator][0]); + + /* Store the section node. */ + untypedSections.insert(std::make_pair(*nameIterator, + sectionNode)); + } + } + + /* Typed sections! */ + for (nameIterator=validTypedSectionNames.begin(); + nameIterator!=validTypedSectionNames.end(); nameIterator++) + { + /* First time here, so we need to create the submap for this sort + * of section. */ + typedSections.insert( + std::make_pair(*nameIterator, + std::map())); + + /* There can be many of these, and we want to store them all. */ + for (nodeIterator=sectionsByName[*nameIterator].begin(); + nodeIterator!=sectionsByName[*nameIterator].end(); + nodeIterator++) + { + /* Get the section node from the section name node. */ + sectionNode = FndSect(sectionsByName[*nameIterator][0]); + + /* Drop it into the submap, indexed by the type of the + * section. */ + typedSections[*nameIterator].insert( + std::make_pair((*nodeIterator)->leaf[0]->str, + sectionNode)); + } + + } + + /* None of the rules were broken (decided when we entered this + * conditional block), so the validation passed. */ + return true; + } + + /* Otherwise, our validation failed, and we have not wasted time populating + * our data structures. */ + else {return false;} +} + +/* Validate type defaults, and populate the defaultTypes map with section + * references for each section that can create items. + * + * Returns true if all validation checks pass, and false otherwise */ +bool HardwareFileReader::d3_validate_types_define_cache() +{ + bool anyErrors; + + /* Container to hold default sections for boxes, boards, and mailboxes. */ + UIF::Node* globalDefaults[ITEM_ENUM_LENGTH] = {PNULL, PNULL, PNULL}; + + /* Populate our new friend. */ + anyErrors = !d3_get_validate_default_types(globalDefaults); + + /* Staging vector and iterator for records. */ + std::vector recordNodes; + std::vector::iterator recordIterator; + + /* Staging vectors for holding value nodes and variable nodes. */ + std::vector valueNodes; + std::vector variableNodes; + std::string type; + unsigned typeLine; + + /* Get all sections that create items, and thus may define +type fields + * (i.e. engine_box, engine_board, and any board(X) section), and store + * pointers to them in creativeSections. */ + std::vector creativeSections; + std::vector::iterator sectionIterator; + creativeSections.push_back(untypedSections["engine_box"]); + creativeSections.push_back(untypedSections["engine_board"]); + + std::map::iterator typedSectionsIterator; + for (typedSectionsIterator=typedSections["board"].begin(); + typedSectionsIterator!=typedSections["board"].end(); + typedSectionsIterator++) + { + creativeSections.push_back(typedSectionsIterator->second); + } + + /* For each of these sections, find the section that defines properties of + * items created within it, and insert this information into + * defaultTypes. */ + bool typeFieldFound; + for (sectionIterator=creativeSections.begin(); + sectionIterator!=creativeSections.end(); sectionIterator++) + { + typeFieldFound = false; + sectionName = (*sectionIterator)->leaf[0]->leaf[0]->str; + + /* Has it got a type-defining record? If so, grab the type by iterating + * through each record in this section. If there's more than one type + * record, it's an error, but just keep soldiering on (this is okay; + * the calling method should be checking the return value of this + * method). */ + GetRecd(*sectionIterator, recordNodes); + for (recordIterator=recordNodes.begin(); + recordIterator!=recordNodes.end(); recordIterator++) + { + record = *recordIterator; + + /* Get the value and variable nodes. */ + GetVari(record, variableNodes); + GetValu(record, valueNodes); + + /* Ignore this record if the record has not got a variable/value + * pair (i.e. if the line is empty, or is just a comment). */ + if (variableNodes.size() == 0 and + valueNodes.size() == 0){continue;} + + /* Is the variable name "type"? */ + if (variableNodes[0]->str == "type") + { + /* Complaints */ + if (!(complain_if_variable_not_plus_prefixed(variableNodes[0]) + and + complain_if_record_is_multivariable(&variableNodes) and + complain_if_record_is_multivalue(&valueNodes))) + { + anyErrors = true; + continue; + } + + /* We got it! But complain if we've already found a type field + * in this way. */ + if (typeFieldFound) + { + errors.push_back(dformat( + "L%u: Duplicate definition of field 'type' in the " + "'%s' section (previously defined at L%u).", + record->pos, sectionName.c_str(), typeLine)); + anyErrors = true; + continue; + } + + typeFieldFound = true; + type = valueNodes[0]->str; + typeLine = record->pos; + } + + /* The variable of this node wasn't named "type", so we ignore it + * for our current machinations. */ + else {continue;} + } + + /* Figure out what sort of object (i.e. box, board...) is being created + * in this section from the section name. */ + std::string itemType; + unsigned arrayIndex; + if (sectionName == "engine_box") + { + itemType = "box"; + arrayIndex = box; + } + else if (sectionName == "engine_board") + { + itemType = "board"; + arrayIndex = board; + } + else /* Must me a [board(something)] section. */ + { + itemType = "mailbox"; + arrayIndex = mailbox; + } + + /* Earlier, did we find a +type record in this section? */ + UIF::Node* sectionTarget = 0; + if (typeFieldFound) /* Yes! */ + { + /* Get the section that matches this type. If there isn't one, + * complain, but keep going (we can use globalDefaults). */ + typedSectionsIterator = typedSections[itemType].find(type); + if (typedSectionsIterator != typedSections[itemType].end()) + { + sectionTarget = typedSectionsIterator->second; + } + else + { + errors.push_back(dformat( + "L%u: Type '%s' defined in the '%s' section does not " + "correspond to a section.", + typeLine, type.c_str(), sectionName.c_str())); + anyErrors = true; + } + } + + /* If there was no +type record in this section, or if the +type was + * invalid, use the one from globalDefaults (even if it is PNULL). */ + if (sectionTarget == 0) + { + sectionTarget = globalDefaults[arrayIndex]; + } + + /* Apply the section found (even if it is PNULL) to defaultTypes. */ + defaultTypes[*sectionIterator] = sectionTarget; + } + + return !anyErrors; +} diff --git a/Source/OrchBase/HardwareFileReader/HardwareFileReaderException.h b/Source/OrchBase/HardwareFileReader/HardwareFileReaderException.h new file mode 100644 index 00000000..353b4954 --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/HardwareFileReaderException.h @@ -0,0 +1,15 @@ +#ifndef __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_HARDWAREFILEREADEREXCEPTION_H +#define __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_HARDWAREFILEREADEREXCEPTION_H + +/* Describes an exception that is to be thrown from the reader (intended as a + * base class). */ + +#include "OrchestratorException.h" +class HardwareFileReaderException: public OrchestratorException +{ +public: + HardwareFileReaderException(std::string message): + OrchestratorException(message){}; +}; + +#endif diff --git a/Source/OrchBase/HardwareFileReader/HardwareSemanticException.h b/Source/OrchBase/HardwareFileReader/HardwareSemanticException.h new file mode 100644 index 00000000..433bc5ca --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/HardwareSemanticException.h @@ -0,0 +1,16 @@ +#ifndef __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_HARDWARESEMANTICEXCEPTION_H +#define __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_HARDWARESEMANTICEXCEPTION_H + +/* Describes an exception that is to be thrown when a hardware file is passed + * to the hardware file reader, and when the file contains a semantic error. */ + +#include "HardwareFileReaderException.h" + +class HardwareSemanticException: public HardwareFileReaderException +{ +public: + HardwareSemanticException(std::string message): + HardwareFileReaderException(message){}; +}; + +#endif diff --git a/Source/OrchBase/HardwareFileReader/HardwareSyntaxException.h b/Source/OrchBase/HardwareFileReader/HardwareSyntaxException.h new file mode 100644 index 00000000..a28efd22 --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/HardwareSyntaxException.h @@ -0,0 +1,16 @@ +#ifndef __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_HARDWARESYNTAXEXCEPTION_H +#define __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_HARDWARESYNTAXEXCEPTION_H + +/* Describes an exception that is to be thrown when a hardware file is passed + * to the hardware file reader, and when the file contains a syntax error. */ + +#include "HardwareFileReaderException.h" + +class HardwareSyntaxException: public HardwareFileReaderException +{ +public: + HardwareSyntaxException(std::string message): + HardwareFileReaderException(message){}; +}; + +#endif diff --git a/Source/OrchBase/HardwareFileReader/Validator.cpp b/Source/OrchBase/HardwareFileReader/Validator.cpp new file mode 100644 index 00000000..b54a862e --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/Validator.cpp @@ -0,0 +1,370 @@ +/* Defines node validation mechanism (see the accompanying + * header for further information). */ + +#include "Validator.h" + +/* Returns whether all of the mandatory strings (elements of mandatoryFields) + * map to true in the fieldsFound map, and false otherwise (even if the string + * is not in the map's keys). Appends an error message to a string for each + * field not found. + * + * But really, just conveniently checks that all fields have been defined in a + * section, to reduce copypasta. + * + * Arguments: + * + * - mandatoryFields: Strings in this vector denote the fields that must be + * defined in this section. + * - fieldsFound: Whether or not the fields in mandatoryFields have been + * defined. May contain strings that are not in mandatoryFields. */ +bool Validator::complain_if_mandatory_field_not_defined( + std::vector* mandatoryFields, + std::map* fieldsFound) +{ + bool returnValue = true; /* Innocent until proven guilty. */ + std::vector::iterator fieldIterator; + std::map::iterator mapRecord; + + bool isThisFieldTrue; + for (fieldIterator=mandatoryFields->begin(); + fieldIterator!=mandatoryFields->end(); fieldIterator++) + { + /* Mark as a failure if either the record in the map was false, or we + * couldn't find the record. */ + isThisFieldTrue = true; /* Also innocent until proven guilty. */ + + /* Is the field a key in the map? */ + mapRecord = fieldsFound->find(*fieldIterator); + if (mapRecord != fieldsFound->end()) /* We got him, boss. */ + { + if (mapRecord->second == false) + { + /* The fields is false in the map. */ + isThisFieldTrue = false; + } + } + else /* Key wasn't in the map. */ + { + isThisFieldTrue = false; + } + + /* Armageddon is here. https://www.youtube.com/watch?v=lcB58I7gSyA */ + if (!isThisFieldTrue) + { + errors.push_back(dformat("Variable '%s' not defined in the '%s' " + "section.", + (*fieldIterator).c_str(), + sectionName.c_str())); + returnValue = false; + } + } + + return returnValue; +} + +/* Returns whether the variable held by a node has a plux prefix, and appends + * an error message to a string if it is not. Arguments: + * + * - variableNode: The UIF node that holds the variable. */ +bool Validator::complain_if_variable_not_plus_prefixed(UIF::Node* variableNode) +{ + if (!does_node_variable_have_plus_prefix(variableNode)) + { + errors.push_back(dformat( + "L%u: Variable in record in the '%s' section is not prefixed by a " + "'+' character.", record->pos, sectionName.c_str())); + return false; + } + return true; +} + +/* Returns whether the value at a node is a valid type, and appends an error + * message to a string if it is not. Arguments: + * + * - valueNode: The UIF node that holds the value. */ +bool Validator::complain_if_value_not_a_valid_type(UIF::Node* valueNode) +{ + if (!is_type_valid(valueNode)) + { + errors.push_back(dformat( + "L%u: Value in record '%s' in the '%s' section is not a valid " + "type (it must satisfy %s).", record->pos, + valueNode->str.c_str(), sectionName.c_str(), TYPE_REGEX)); + return false; + } + return true; +} + +/* Returns whether the value at a node is a positive floating-point number (or + * an integer), and appends an error message to a string if it is + * not. Arguments: + * + * - valueNode: The UIF node that holds the value. */ +bool Validator::complain_if_value_not_floating(UIF::Node* valueNode) +{ + if (!is_node_value_floating(valueNode)) + { + errors.push_back(dformat( + "L%u: Variable '%s' in the '%s' section has value '%s', which is " + "not a positive floating-point number or a positive integer.", + record->pos, variable.c_str(), sectionName.c_str(), + valueNode->str.c_str())); + return false; + } + return true; +} + +/* Returns whether the value at a node is a natural number, and appends an + * error message to a string if it is not. Arguments: + * + * - valueNode: The UIF node that holds the value. */ +bool Validator::complain_if_value_not_natural(UIF::Node* valueNode) +{ + if (!is_node_value_natural(valueNode)) + { + errors.push_back(dformat( + "L%u: Variable '%s' in the '%s' section has value '%s', which is " + "not a natural number.", + record->pos, variable.c_str(), sectionName.c_str(), + valueNode->str.c_str())); + return false; + } + return true; +} + +/* Returns whether the value at a node, and all of its children, are natural + * numbers, and appends an error message to a string if they are + * not. Arguments: + * + * - valueNode: The UIF node that holds the value. */ +bool Validator::complain_if_values_and_children_not_natural( + UIF::Node* valueNode) +{ + std::vector::iterator valueNodeIterator; + bool valid = true; + for (valueNodeIterator=valueNode->leaf.begin(); + valueNodeIterator!=valueNode->leaf.end(); valueNodeIterator++) + { + valid &= complain_if_value_not_natural(*valueNodeIterator); + } + return valid; +} + +/* Returns whether the variable at a node is a valid item name, and appends an + * error message to a string if it is not. Arguments: + * + * - variableNode: The UIF node that holds the variable. */ +bool Validator::complain_if_variable_not_a_valid_item_name( + UIF::Node* variableNode) +{ + if (!is_type_valid(variableNode)) /* It's the same rule! */ + { + errors.push_back(dformat( + "L%u: Item name '%s' is not a valid item name (it must satisfy " + "%s).", record->pos, variableNode->str.c_str(), TYPE_REGEX)); + return false; + } + return true; +} + +/* Returns whether the variable at a node maps to true, and appends an error + * message if it does so. Arguments: + * + * - variableNode: The UIF node that holds the variable. + * - mapToSearch: The map, mapping names of variables, to a boolean. In most + * cases where this is used, this boolean represents whether or not the name + * has already been defined in this pass of a section before. */ +bool Validator::complain_if_variable_true_in_map( + UIF::Node* variableNode, std::map* mapToSearch) +{ + if(is_node_variable_true_in_map(mapToSearch, variableNode)) + { + errors.push_back(dformat( + "L%u: Duplicate definition of variable '%s' in the '%s' section.", + record->pos, sectionName.c_str())); + return true; + } + return false; +} + +/* As with complain_if_record_is_multivariable, but for values and GetValu + * (which are handled slightly dirrerently). Arguments: + * + * - valueNodes: Pointer to a vector holding all value nodes (probably + * obtained using JNJ::GetValu). */ +bool Validator::complain_if_record_is_multivalue( + std::vector* valueNodes) +{ + if (is_multivalue_record(valueNodes)) + { + errors.push_back(dformat( + "L%u: Variable '%s' in the '%s' section is invalid because it " + "defines zero, or multiple, values.", + record->pos, variable.c_str(), sectionName.c_str())); + return false; + } + return true; +} + +/* Returns true if the variable vector obtained from a record (using GetVari) + * is single-variable, or false if it is multi-variable or does not define a + * variable, and appends an error message to a string if false. Arguments: + * + * - variableNodes: Pointer to a vector holding all variable nodes (probably + * obtained using JNJ::GetVari). */ +bool Validator::complain_if_record_is_multivariable( + std::vector* variableNodes) +{ + if (is_multivariable_record(variableNodes)) + { + errors.push_back(dformat( + "L%u: Record in the '%s' section is invalid because it defines " + "zero, or multiple, variables.", + record->pos, sectionName.c_str())); + return false; + } + return true; +} + +/* Returns whether the variable at a node is in a given vector of valid + * variable names, and appends an error message to a string if it is + * not. Arguments: + * + * - variableNode: The UIF node that holds the variable. + * - validFields: Strings denoting valid field names. */ +bool Validator::complain_if_variable_name_invalid( + UIF::Node* variableNode, std::vector* validFields) +{ + if (!is_variable_name_valid(validFields, variableNode)) + { + errors.push_back(dformat( + "L%u: Variable name '%s' is not valid in the '%s' section.", + record->pos, variableNode->str.c_str(), sectionName.c_str())); + return false; + } + return true; +} + +/* Returns whether the variable at a node is prefixed with a "+" + * token. Arguments: + * + * - varaibleNode: The UIF node that holds the variable. */ +bool Validator::does_node_variable_have_plus_prefix(UIF::Node* variableNode) +{ + return (variableNode->qop == Lex::Sy_plus); +} + +/* Returns true if the value vector obtained from a record is multi-valued or + * has no value, and false otherwise. Arguments: + * + * - valueNodes: All of the variables associated with a record (probably + * obtained using JNJ::GetValu). */ +bool Validator::is_multivalue_record(std::vector* valueNodes) +{ + if (valueNodes->size() > 0) /* Are there any values? */ + { + /* If there is only one value, the first value node contains the + * value. If there are multiple (N) value nodes, the first value node + * contains N value nodes, each with an entry. */ + return ((*valueNodes)[0]->leaf.size() != 0); + } + return true; +} + +/* Returns true if the variable vector obtained from a record is + * multi-variable or has no value, and false otherwise. Arguments: + * + * - variableNodes: All of the variables associated with a record (probably + * obtained using JNJ::GetVari). */ +bool Validator::is_multivariable_record(std::vector* variableNodes) +{ + return (variableNodes->size() != 1); +} + +/* Returns whether the value at a node is a positive floating-point + * number. Arguments: + * + * - valueNode: The UIF node that holds the value. */ +bool Validator::is_node_value_floating(UIF::Node* valueNode) +{ + return (valueNode->qop == Lex::Sy_FSTR or valueNode->qop == Lex::Sy_ISTR); +} + +/* Returns whether the value at a node is a natural number. Arguments: + * + * - valueNode: The UIF node that holds the value. */ +bool Validator::is_node_value_natural(UIF::Node* valueNode) +{ + return (valueNode->qop == Lex::Sy_ISTR); +} + +/* Returns whether the variable at a node maps to true. Arguments: + * + * - mapToSearch: The map, mapping names of variables, to a boolean. In most + * cases where this is used, this boolean represents whether or not the name + * has already been defined in this pass of a section before. + * - variableNode: The UIF node that holds the variable. + * + * Will throw if the map does not contain variableNode's string name. */ +bool Validator::is_node_variable_true_in_map( + std::map* mapToSearch, UIF::Node* variableNode) +{ + return mapToSearch->at(variableNode->str); +} + +/* Returns whether the name at a node is a valid type name (i.e. must satisfy + * [0-9A-Za-z]{2,32}. Arguments: + * + * - nameNode: The UIF node that holds the name. */ +bool Validator::is_type_valid(UIF::Node* nameNode) +{ + std::string toValidate = nameNode->str; + + /* Check length. */ + if (toValidate.size() < 2 or toValidate.size() > 32) + { + return false; + } + + /* Check for alphanumeric-ness. */ + for (unsigned zI=0; zI* validFields, + UIF::Node* variableNode) +{ + return (std::find(validFields->begin(), validFields->end(), + variableNode->str) != validFields->end()); +} + +/* Converts a float-like input string to an actual float. */ +float str2float(std::string floatLike) +{ + float returnValue; + sscanf(floatLike.c_str(), "%f", &returnValue); + return returnValue; +} + +/* Converts a float-like input string to an actual float. I know something like + * this exists in flat, but I want something with a single argument so that I + * can use std::transform to transform a vector of strings into a vector of + * unsigneds. */ +unsigned str2unsigned(std::string uintLike) +{ + unsigned returnValue; + sscanf(uintLike.c_str(), "%u", &returnValue); + return returnValue; +} diff --git a/Source/OrchBase/HardwareFileReader/Validator.h b/Source/OrchBase/HardwareFileReader/Validator.h new file mode 100644 index 00000000..16f183af --- /dev/null +++ b/Source/OrchBase/HardwareFileReader/Validator.h @@ -0,0 +1,67 @@ +#ifndef __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_VALIDATOR_H +#define __ORCHESTRATOR_SOURCE_ORCHBASE_HARDWAREFILEREADER_VALIDATOR_H + +/* Logic for defining validation methods for nodes and other data structures + * (which write error messages), and simple functions to convert strings to + * intelligible values. */ + +#include "dfprintf.h" +#include "flat.h" +#include "uif.h" + +/* Used for error messages. */ +#define TYPE_REGEX "[0-9A-Za-z]{2,32}" + +class Validator +{ +public: + /* Holds errors. */ + std::vector errors; + + /* Helper members that must be set by the caller in order to have helpful + * error messages. This is largely a convenience to stop repeated lookups + * in the tree. */ + UIF::Node* record; + std::string sectionName; + std::string variable; /* The name */ + + /* Validators that write error messages. */ + bool complain_if_mandatory_field_not_defined( + std::vector* mandatoryFields, + std::map* fieldsFound); + bool complain_if_variable_not_plus_prefixed(UIF::Node* variableNode); + bool complain_if_value_not_a_valid_type(UIF::Node* valueNode); + bool complain_if_value_not_floating(UIF::Node* valueNode); + bool complain_if_value_not_natural(UIF::Node* valueNode); + bool complain_if_values_and_children_not_natural(UIF::Node* valueNode); + bool complain_if_variable_not_a_valid_item_name(UIF::Node* variableNode); + bool complain_if_variable_true_in_map( + UIF::Node* variableNode, + std::map* mapToSearch); + bool complain_if_record_is_multivalue(std::vector* valueNodes); + bool complain_if_record_is_multivariable( + std::vector* variableNodes); + bool complain_if_variable_name_invalid( + UIF::Node* variableNode, + std::vector* validVariables); + + /* Validators */ + bool does_node_variable_have_plus_prefix(UIF::Node* variableNode); + bool is_multivalue_record(std::vector* valueNodes); + bool is_multivariable_record(std::vector* variableNodes); + + bool is_node_value_floating(UIF::Node* valueNode); + bool is_node_value_natural(UIF::Node* valueNode); + bool is_node_variable_true_in_map(std::map* mapToSearch, + UIF::Node* variableNode); + bool is_type_valid(UIF::Node* nameNode); + bool is_variable_name_valid(std::vector* validFields, + UIF::Node* variableNode); + +}; + +/* Converters */ +float str2float(std::string floatLike); +unsigned str2unsigned(std::string unsignedLike); + +#endif diff --git a/Source/OrchBase/OrchBase.h b/Source/OrchBase/OrchBase.h index 04d93d11..e01b89a6 100644 --- a/Source/OrchBase/OrchBase.h +++ b/Source/OrchBase/OrchBase.h @@ -12,7 +12,7 @@ class Dialect1Deployer; #include #include "D_graph.h" #include "HardwareModel.h" -#include "HardwareFileParser.h" +#include "HardwareFileReader.h" #include "Placement.h" #include "Cli.h" #include "Environment.h" @@ -20,6 +20,8 @@ class Dialect1Deployer; #include "P_owner.h" using namespace std; +#define TASK_DEPLOY_DIR ".orchestrator/task_binaries" // Relative to home. + //============================================================================== class OrchBase : public CommonBase, public NameBase diff --git a/Source/OrchBase/OrchBaseLink.cpp b/Source/OrchBase/OrchBaseLink.cpp index 9d976abd..63f78b7d 100644 --- a/Source/OrchBase/OrchBaseLink.cpp +++ b/Source/OrchBase/OrchBaseLink.cpp @@ -210,6 +210,7 @@ if (!pPlace->Place(pT)) // (sAddr.A_device << P_DEVICE_OS); supervisorAddrs.push_back((sAddr.A_box << P_BOX_OS) | (sAddr.A_board << P_BOARD_OS) | + (sAddr.A_mailbox << P_MAILBOX_OS) | (sAddr.A_core << P_CORE_OS) | (sAddr.A_thread << P_THREAD_OS) | (sAddr.A_device << P_DEVICE_OS)); @@ -279,6 +280,7 @@ if (!pPlace->Place(pT)) // (dAddr.A_device << P_DEVICE_OS); devAddrs.push_back((dAddr.A_box << P_BOX_OS) | (dAddr.A_board << P_BOARD_OS) | + (dAddr.A_mailbox << P_MAILBOX_OS) | (dAddr.A_core << P_CORE_OS) | (dAddr.A_thread << P_THREAD_OS) | (dAddr.A_device << P_DEVICE_OS)); diff --git a/Source/OrchBase/OrchBaseTask.cpp b/Source/OrchBase/OrchBaseTask.cpp index 15a9dc9a..291cd91e 100644 --- a/Source/OrchBase/OrchBaseTask.cpp +++ b/Source/OrchBase/OrchBaseTask.cpp @@ -401,7 +401,7 @@ if (Cl.Pa_v.size()!=1) { // Command make sense? Post(47,Cl.Cl,"task","1"); return; } -string file = Cl.GetP(0); // Unpack file name +string file = Cl.Pa_v[0].Val; // Unpack file name file = taskpath + file; // Add default full file path FileName Fn(file); // Make sure it's a semantic filename if (Fn.Err()) Post(104,file); // If not, warn the monkey @@ -579,16 +579,15 @@ vector::iterator currBox = pPmap[cIdx]->vPmap.begin(); // pr CMsg_p PktC; // message to send to each participating box -PktC.Key(Q::NAME,Q::CFG,Q::DIST); // distributing the core mappings and the data directory +PktC.Key(Q::NAME,Q::CFG,Q::DIST); // distributing the core mappings and the data directory PktC.Src(Urank); string taskname = task->first; PktC.Zname(0, taskname); // first string field in the packet is the task name //PktC.Put(0, &taskname); // first field in the packet is the task name -taskname+="/"; // for the moment the binary directory will be fixed -taskname+=BIN_PATH; // later we could make this user-settable. // duplicate P_builder's iteration through the task P_core* thisCore; // Core available during iteration. P_thread* firstThread; // The "first" thread in thisCore. "first" is arbitrary, because cores are stored in a map. +bool isTaskMappedToThisBox; WALKMAP(AddressComponent, P_box*, pE->P_boxm, boxNode) { while (cIdx < Comms.size()) // grab the next available mothership @@ -597,8 +596,8 @@ while (cIdx < Comms.size()) // grab the next available mothership if (currBox != pPmap[cIdx]->vPmap.end()) break; ++cIdx; } -taskname.insert(0,string("/home/")+currBox->P_user+"/"); -PktC.Zname(1,taskname); // second string field is the task binary directory + +isTaskMappedToThisBox = false; coreVec.clear(); // reset the packet content WALKVECTOR(P_board*,boxNode->second->P_boardv,board) { @@ -613,6 +612,7 @@ thisCore = core->second; firstThread = thisCore->P_threadm.begin()->second; if (firstThread->P_devicel.size() && (firstThread->P_devicel.front()->par->par == task->second)) // only for cores which have something placed on them and which belong to the task { + isTaskMappedToThisBox = true; // determine the last thread that has a device mapped onto it (recall that all devices within a core service the same task. std::map::reverse_iterator thread; for (thread=thisCore->P_threadm.rbegin(); @@ -650,26 +650,58 @@ if ((cIdx >= Comms.size()) && coreVec.size()) // not enough Motherships have rep return; } +if (isTaskMappedToThisBox) +{ // same machine running Root and Mothership? (Tediously this requires searching the // process maps linearly because there is no method within a ProcMap to get the // index of the entry which is Root) int RootIndex = RootCIdx(); vector::iterator RootProcMapI = pPmap[RootIndex]->vPmap.begin(); while (RootProcMapI->P_rank != pPmap[RootIndex]->U.Root) RootProcMapI++; + +// stages commands for execution +std::vector commands; +// where the binaries are (target is where the binaries will go). The asterisk +// is a Bash glob (thanks system!) +std::string target = string("/home/") + currBox->P_user + "/" + + TASK_DEPLOY_DIR + "/" + task->first; +std::string sourceBins = taskpath + task->first + "/" + BIN_PATH + "/*"; +PktC.Zname(1,target); // second string field is the task binary directory if (RootProcMapI->P_proc == currBox->P_proc) { // then copy locally (inefficient, wasteful, using the files in place would be better but this would require // different messages to be sent to different Motherships. For a later revision. - system((string("rm -r -f /home/")+currBox->P_user+"/"+task->first).c_str()); - system((string("mkdir /home/")+currBox->P_user+"/"+task->first).c_str()); - system((string("cp -r ")+taskpath+task->first+"/"+BIN_PATH+" "+taskname).c_str()); + commands.push_back(string("rm -r -f ") + target); + commands.push_back(string("mkdir -p ") + target); + commands.push_back(string("cp -r ") + sourceBins + " " + target); } else { // otherwise copy binaries to the Mothership using SCP. This assumes ssh-agent has been run for the user. - system((string("ssh ")+currBox->P_user+"@"+currBox->P_proc+ "\"rm -r -f "+task->first+"\"").c_str()); - system((string("ssh ")+currBox->P_user+"@"+currBox->P_proc+ "\"mkdir "+task->first+"\"").c_str()); - system((string("scp -r ")+taskpath+task->first+"/"+BIN_PATH+" "+currBox->P_user+"@"+currBox->P_proc+":"+taskname).c_str()); + std::string host = currBox->P_user + "@" + currBox->P_proc; + commands.push_back(string("ssh ") + host + " \"rm -r -f " + target + "\""); + commands.push_back(string("ssh ") + host + " \"mkdir -p " + target + "\""); + commands.push_back(string("scp -r ") + sourceBins + " " + + host + ":" + target); +} + +// run each staged command, failing fast if one of them breaks. +WALKVECTOR(std::string, commands, command) +{ + if (system(command->c_str()) > 0) + { + // Command failed, cancelling deployment. + if (errno == 0) + { + Post(165, command->c_str()); + } + else + { + Post(167, command->c_str(), + POETS::getSysErrorString(errno).c_str()); + } + return; + } } PktC.comm = Comms[cIdx]; // Packet will go on the communicator it was found on Post(726,int2str(currBox->P_rank),uint2str(coreVec.size())); @@ -691,6 +723,7 @@ if (nsComm >= pPmap.size()) // No NameServer. A severe error. Post(711); return; } +} } // Next Mothership } @@ -856,42 +889,52 @@ Pkt.Src(Urank); string taskname = task->first; Pkt.Put(0, &taskname); // first field in the packet is the task name -unsigned cIdx = 0; // comm number to look for Motherships. Start from local MPI_COMM_WORLD. -vector::iterator currBox = pPmap[cIdx]->vPmap.begin(); // process map for the Mothership being deployed to -P_thread* firstThread; // The "first" thread in the core in the current iteration. "first" is arbitrary, because cores are stored in a map. +// search for boxes to deploy to +std::set boxesToDeployTo; +pE->get_boxes_for_task(task->second, &boxesToDeployTo); -// search for boxes assigned to the task. For the future, it would be more efficient to build a map -// rather than redo the search. -WALKMAP(AddressComponent, P_box*, pE->P_boxm, boxNode) -{ -while (cIdx < Comms.size()) // grab the next available mothership -{ - while ((currBox != pPmap[cIdx]->vPmap.end()) && (currBox->P_class != csMOTHERSHIPproc)) ++currBox; - if (currBox != pPmap[cIdx]->vPmap.end()) break; - ++cIdx; +// search for mothership processes. For now, we naively ignore whether the +// mothership matches the box in the hardware graph. This is bad, because: +// +// - motherships could be sitting on different hardware configurations +// +// - tasks already deployed to motherships are not accounted for +// +// the first element of this pair is the communicator, and the second element +// is the rank. +std::vector> mothershipsToDeployTo; +std::vector::iterator currBox; +// iterate over communicators +for (unsigned comm=0; commvPmap.begin(); + currBox!=pPmap[comm]->vPmap.end(); currBox++) + { + // store it if it's a mothership + if (currBox->P_class == csMOTHERSHIPproc) + { + mothershipsToDeployTo.push_back( + std::make_pair(comm, currBox->P_rank)); + } + } } -bool UsedByTask = false; -WALKVECTOR(P_board*,boxNode->second->P_boardv,board) -{ -WALKPDIGRAPHNODES(AddressComponent, P_mailbox*, - unsigned, P_link*, - unsigned, P_port*, (*board)->G, mailbox) -{ -WALKMAP(AddressComponent, P_core*, - (*board)->G.NodeData(mailbox)->P_corem, core) -{ -firstThread = core->second->P_threadm.begin()->second; -if (firstThread->P_devicel.size() && (firstThread->P_devicel.front()->par->par == task->second)) // only for cores which have something placed on them and which belong to the task + +// if there aren't enough motherships for the number of boxes we want to deploy +// to, someone's made a mistake (either the hardware model is wrong, or a +// mothership has died). Either way, we out, yo. +if (mothershipsToDeployTo.size() < boxesToDeployTo.size()) { - Pkt.comm = Comms[cIdx]; // Packet will go on the communicator it was found on - Pkt.Send(currBox->P_rank); // to the target Mothership. This will work for now, but it would be far better to broadcast the send (so everyone gets it synchronously) - UsedByTask = true; - break; + Post(164, task->first.c_str(), TO_STRING(boxesToDeployTo.size()).c_str(), + TO_STRING(mothershipsToDeployTo.size()).c_str()); + return; } -} -} -} -if (UsedByTask) break; // only need to send once to each box + +// for each box, deploy to a mothership +for (unsigned boxIndex=0; boxIndexNpar(this); std::string inputFilePath = Cl.Pa_v[0].Val; - HardwareFileParser parser; + HardwareFileReader reader; try { - parser.load_file(inputFilePath.c_str()); - parser.populate_hardware_model(pE); + reader.load_file(inputFilePath.c_str()); + reader.populate_hardware_model(pE); Post(140, inputFilePath.c_str()); pPlace->Init(); } diff --git a/Source/OrchBase/P_builder.cpp b/Source/OrchBase/P_builder.cpp index 8c5572ab..cdac4f2e 100644 --- a/Source/OrchBase/P_builder.cpp +++ b/Source/OrchBase/P_builder.cpp @@ -266,84 +266,92 @@ unsigned P_builder::GenFiles(P_task* task) // handlers_.h // handlers_.cpp // - // build a core map visible to the make script (as a shell script) + // Build a core map visible to the make script (as a shell script). Each of + // the cores is uniquely identified within a board. //============================================================================ P_core* thisCore; // Core available during iteration. P_thread* firstThread; // The "first" thread in thisCore. "first" is arbitrary, because cores are stored in a map. + AddressComponent mailboxCoreId; // Concatenated mailbox-core address (staging area) fstream cores_sh((task_dir+GENERATED_PATH+"/cores.sh").c_str(), fstream::in | fstream::out | fstream::trunc); // Open the cores shell script - - // Walk through all of the boards in the graph - WALKPDIGRAPHNODES(AddressComponent,P_board*,unsigned,P_link*, - unsigned,P_port*,par->pE->G,boardNode) + + // Walk through all of the boxes in the system + WALKMAP(AddressComponent, P_box*, par->pE->P_boxm, boxNode) { - - // Walk through all of the mailboxes on the board. - WALKPDIGRAPHNODES(AddressComponent,P_mailbox*,unsigned,P_link*,unsigned, - P_port*,par->pE->G.NodeData(boardNode)->G,mailboxNode) - { - - // Walk through all of the cores on the mailbox - WALKMAP(AddressComponent,P_core*, - par->pE->G.NodeData(boardNode)->G.NodeData(mailboxNode)->P_corem, - coreNode) - { - thisCore = coreNode->second; // Reference to the current core - firstThread = thisCore->P_threadm.begin()->second; // Reference to the first thread on the core + // Walk through all of the boards in a box + WALKVECTOR(P_board*,boxNode->second->P_boardv,boardNode) + { + // Walk through all of the mailboxes on the board. + WALKPDIGRAPHNODES(AddressComponent,P_mailbox*,unsigned,P_link*,unsigned, + P_port*,(*boardNode)->G,mailboxNode) + { - if (firstThread->P_devicel.size() // only for cores with something placed - && (firstThread->P_devicel.front()->par->par == task)) // and that belong to the task - { - cores_sh << "cores[" << coreNum << "]="; - cores_sh << thisCore->get_hardware_address()->get_core() << "\n"; - // these consist of the declarations and definitions of variables and the handler functions. - - //==================================================================== - // Create empty files for the per-core variables declarations - //==================================================================== - std::stringstream vars_hFName; - vars_hFName << task_dir << GENERATED_H_PATH; - vars_hFName << "/vars_" << coreNum << ".h"; - std::ofstream vars_h(vars_hFName.str()); // variables header - - - //==================================================================== - + // Walk through all of the cores on the mailbox + WALKMAP(AddressComponent,P_core*, + (*boardNode)->G.NodeData(mailboxNode)->P_corem, + coreNode) + { + thisCore = coreNode->second; // Reference to the current core + firstThread = thisCore->P_threadm.begin()->second; // Reference to the first thread on the core - //==================================================================== - // Write core vars. - //==================================================================== - if (WriteCoreVars(task_dir, coreNum, thisCore, firstThread, vars_h)) - { // Writing core vars failed - bail - vars_h.close(); - cores_sh.close(); - return 1; - } - //==================================================================== - - - //==================================================================== - // Generate thread variables - //==================================================================== - WALKMAP(AddressComponent,P_thread*,thisCore->P_threadm,threadIterator) + if (firstThread->P_devicel.size() // only for cores with something placed + && (firstThread->P_devicel.front()->par->par == task)) // and that belong to the task { - if (threadIterator->second->P_devicel.size()) + mailboxCoreId = thisCore->get_hardware_address()->get_mailbox() \ + << par->pE->addressFormat.coreWordLength; + mailboxCoreId += thisCore->get_hardware_address()->get_core(); + + cores_sh << "cores[" << coreNum << "]="; + cores_sh << mailboxCoreId << "\n"; + // these consist of the declarations and definitions of variables and the handler functions. + + //==================================================================== + // Create empty files for the per-core variables declarations + //==================================================================== + std::stringstream vars_hFName; + vars_hFName << task_dir << GENERATED_H_PATH; + vars_hFName << "/vars_" << coreNum << ".h"; + std::ofstream vars_h(vars_hFName.str().c_str()); // variables header + + + //==================================================================== + + + //==================================================================== + // Write core vars. + //==================================================================== + if (WriteCoreVars(task_dir, coreNum, thisCore, firstThread, vars_h)) + { // Writing core vars failed - bail + vars_h.close(); + cores_sh.close(); + return 1; + } + //==================================================================== + + + //==================================================================== + // Generate thread variables + //==================================================================== + WALKMAP(AddressComponent,P_thread*,thisCore->P_threadm,threadIterator) { - if(WriteThreadVars(task_dir, coreNum, threadIterator->first, - threadIterator->second, vars_h)) - { // Writing thread vars failed - bail - vars_h.close(); - cores_sh.close(); - return 1; + if (threadIterator->second->P_devicel.size()) + { + if(WriteThreadVars(task_dir, coreNum, threadIterator->first, + threadIterator->second, vars_h)) + { // Writing thread vars failed - bail + vars_h.close(); + cores_sh.close(); + return 1; + } } } + //==================================================================== + + + vars_h.close(); // close the core's declarations + ++coreNum; // move on to the next core. } - //==================================================================== - - - vars_h.close(); // close the core's declarations - ++coreNum; // move on to the next core. } } } @@ -403,8 +411,9 @@ unsigned P_builder::GenSupervisor(P_task* task) //========================================================================== std::stringstream supervisor_hFName; supervisor_hFName << task_dir << "/" << GENERATED_PATH <<"/Supervisor.h"; - std::ofstream supervisor_h(supervisor_hFName.str(), fstream::app); // Supervisor header, open in append - + std::ofstream supervisor_h(supervisor_hFName.str().c_str(), + fstream::app); // Supervisor header, open in append + if(supervisor_h.fail()) // Check that the file opened { // if it didn't, tell logserver and exit par->Post(816, supervisor_hFName.str(), POETS::getSysErrorString(errno)); @@ -413,7 +422,8 @@ unsigned P_builder::GenSupervisor(P_task* task) std::stringstream supervisor_cFName; supervisor_cFName << task_dir << "/" << GENERATED_PATH <<"/Supervisor.cpp"; - std::ofstream supervisor_cpp(supervisor_cFName.str(), fstream::app); // Supervisor code, open in append + std::ofstream supervisor_cpp(supervisor_cFName.str().c_str(), + fstream::app); // Supervisor code, open in append if(supervisor_cpp.fail()) // Check that the file opened { // if it didn't, tell logserver, close .h and exit par->Post(816, supervisor_cFName.str(), POETS::getSysErrorString(errno)); @@ -524,10 +534,38 @@ unsigned P_builder::GenSupervisor(P_task* task) sup_pin_handlers << " return 0;\n"; sup_pin_handlers << "}\n\n"; + + //======================================================================== + // Add the pin's teardown code - this deletes the properties and state if + // they exist. Called by the destructor for supInputPin in Supervisor.cpp + //======================================================================== + sup_pin_handlers << "unsigned super_InPin_" << sIpin_name; + sup_pin_handlers << "_PinTeardown (const void* pinProps, "; + sup_pin_handlers << "void* pinState)\n"; + sup_pin_handlers << "{\n"; + + if ((*sI_pin)->pPropsD) + { + sup_pin_handlers << " delete static_cast(pinProps);\n"; + } + + if ((*sI_pin)->pStateD) + { + sup_pin_handlers << " delete static_cast(pinState);\n"; + } + + sup_pin_handlers << " return 0;\n"; + sup_pin_handlers << "}\n\n"; + //======================================================================== + + // this will create a new pin object - how should this be deleted on exit since it's held in a static class member? sup_pin_vectors << "Supervisor::inputs.push_back"; sup_pin_vectors << "(new supInputPin("; sup_pin_vectors << "&super_InPin_" << sIpin_name << "_Recv_handler,"; + sup_pin_vectors << "&super_InPin_" << sIpin_name << "_PinTeardown,"; sup_pin_vectors << sup_inPin_props.str() << ","; sup_pin_vectors << sup_inPin_state.str(); sup_pin_vectors << "));\n"; @@ -680,7 +718,7 @@ unsigned P_builder::WriteCoreVars(std::string& task_dir, unsigned coreNum, std::stringstream vars_cppFName; vars_cppFName << task_dir << GENERATED_CPP_PATH; vars_cppFName << "/vars_" << coreNum << ".cpp"; - std::ofstream vars_cpp(vars_cppFName.str()); // variables source + std::ofstream vars_cpp(vars_cppFName.str().c_str()); // variables source if(vars_cpp.fail()) // Check that the file opened { // if it didn't, tell logserver and exit par->Post(816, vars_cppFName.str(), POETS::getSysErrorString(errno)); @@ -690,7 +728,7 @@ unsigned P_builder::WriteCoreVars(std::string& task_dir, unsigned coreNum, std::stringstream handlers_hFName; handlers_hFName << task_dir << GENERATED_H_PATH; handlers_hFName << "/handlers_" << coreNum << ".h"; - std::ofstream handlers_h(handlers_hFName.str()); // handlers header + std::ofstream handlers_h(handlers_hFName.str().c_str()); // handlers header if(handlers_h.fail()) // Check that the file opened { // if it didn't, tell logserver and exit par->Post(816, handlers_hFName.str(), POETS::getSysErrorString(errno)); @@ -701,7 +739,7 @@ unsigned P_builder::WriteCoreVars(std::string& task_dir, unsigned coreNum, std::stringstream handlers_cppFName; handlers_cppFName << task_dir << GENERATED_CPP_PATH; handlers_cppFName << "/handlers_" << coreNum << ".cpp"; - std::ofstream handlers_cpp(handlers_cppFName.str()); // handlers source + std::ofstream handlers_cpp(handlers_cppFName.str().c_str()); // handlers source if(handlers_cpp.fail()) // Check that the file opened { // if it didn't, tell logserver and exit par->Post(816, handlers_cppFName.str(), POETS::getSysErrorString(errno)); @@ -1059,7 +1097,7 @@ unsigned P_builder::WriteThreadVars(string& task_dir, unsigned coreNum, std::stringstream vars_cppFName; vars_cppFName << task_dir << GENERATED_CPP_PATH; vars_cppFName << "/vars_" << coreNum << "_" <Post(816, vars_cppFName.str(), POETS::getSysErrorString(errno)); @@ -1310,7 +1348,7 @@ unsigned P_builder::WriteThreadVars(string& task_dir, unsigned coreNum, if (inTypCnt) { std::string thrDevNameIn = "Thread_"; - thrDevNameIn += std::to_string(thread_num) + std::string("_Device_"); + thrDevNameIn += TO_STRING(thread_num) + std::string("_Device_"); thrDevNameIn += (*device)->Name() + std::string("_InputPins"); vars_h << "//----------------------- Input Pin (Associative) Tables "; diff --git a/Source/OrchBase/P_builder.h b/Source/OrchBase/P_builder.h index 9709fd50..f224d3fa 100644 --- a/Source/OrchBase/P_builder.h +++ b/Source/OrchBase/P_builder.h @@ -3,6 +3,7 @@ #include #include "OrchBase.h" +#include "OSFixes.hpp" #include #include #include diff --git a/Source/OrchBase/P_devtyp.cpp b/Source/OrchBase/P_devtyp.cpp index 39daf9ac..3684e38d 100644 --- a/Source/OrchBase/P_devtyp.cpp +++ b/Source/OrchBase/P_devtyp.cpp @@ -18,6 +18,7 @@ pPropsI = 0; pStateI = 0; pPropsD = 0; pStateD = 0; +idx = 0; } //------------------------------------------------------------------------------ diff --git a/Source/OrchBase/P_super.cpp b/Source/OrchBase/P_super.cpp index 6ce9384e..5bd5b69d 100644 --- a/Source/OrchBase/P_super.cpp +++ b/Source/OrchBase/P_super.cpp @@ -55,8 +55,8 @@ void P_super::Attach(P_board* targetBoard) // Get the index of this supervisor in the box that contains this board. P_box* parentBox = targetBoard->parent; std::vector::iterator supervisorIterator; - supervisorIterator = std::find(std::begin(parentBox->P_superv), - std::end(parentBox->P_superv), this); + supervisorIterator = std::find(parentBox->P_superv.begin(), + parentBox->P_superv.end(), this); unsigned supervisorIndex; // We found it! Grab the index. if (supervisorIterator != parentBox->P_superv.end()) @@ -89,8 +89,8 @@ void P_super::Detach(P_board* targetBoard) // Get the index of this supervisor in the box that contains this board. P_box* parentBox = targetBoard->parent; std::vector::iterator supervisorIterator; - supervisorIterator = std::find(std::begin(parentBox->P_superv), - std::end(parentBox->P_superv), this); + supervisorIterator = std::find(parentBox->P_superv.begin(), + parentBox->P_superv.end(), this); // If we didn't find it, it's already been cleared. Let's not worry then. if (supervisorIterator == parentBox->P_superv.end()){return;} diff --git a/Source/OrchBase/Placement.cpp b/Source/OrchBase/Placement.cpp index e090c3df..325e1a3a 100644 --- a/Source/OrchBase/Placement.cpp +++ b/Source/OrchBase/Placement.cpp @@ -45,13 +45,23 @@ void Placement::Init() bool Placement::Place(P_task * pT) // Place a task. { -P_thread* pTh = iterator->get_thread(); +P_thread* pTh; WALKVECTOR(P_devtyp*,pT->pP_typdcl->P_devtypv,dT) { if ((*dT)->pOnRTS) // don't need to place if it's a supervisor - easily // identified by lack of RTS handler { + pTh = iterator->get_thread(); + + // abandon placement if the iterator has wrapped (won't happen outside + // of trivial cases on the first iteration). + if (iterator->has_wrapped()) + { + par->Post(163, pT->Name()); + return true; + } + // get all the devices of this type vector dVs = pT->pD->DevicesOfType(*dT); unsigned int devMem = (*dT)->MemPerDevice(); @@ -98,42 +108,19 @@ WALKVECTOR(P_devtyp*,pT->pP_typdcl->P_devtypv,dT) Xlink(dVs[devIdx],pTh); } - // jump to the next core: each core will only have one device type. We - // do not need to jump if the devices exactly fit on an integral number - // of cores because in that situation the previous GetNext() function - // will have incremented the core for us. - if (dVs.size()%(pCon->Constraintm["DevicesPerThread"]*pCon->Constraintm["ThreadsPerCore"])) - { - if (((dT+1) != pT->pP_typdcl->P_devtypv.end())) - { - // get the first thread on the next core, and abandon if we've - // filled up the hardware. - iterator->next_core(); - pTh = iterator->get_thread(); - if (iterator->has_wrapped()) - { - // out of room. Abandon placement. - par->Post(163, pT->Name()); - return true; - } - } - } - // current tinsel architecture shares I-memory between pairs of cores, - // so for a new device type, if the postincremented core number is odd, - // we need to increment again to get an even boundary. + // we're done with this device type. A device of a different type must + // be placed on a different core. When we reach here, we know our + // current thread is not empty (because we've done a `next_thread`, + // then inserted another device - you could check with if + // (!pTh->P_devicel.empty()) I suppose...) + iterator->next_core(); + // The current tinsel architecture shares I-memory between pairs of + // cores, so for a new device type, if the postincremented core number + // is odd, we need to increment again to get an even boundary. if (SHARED_INSTR_MEM && iterator->get_core()->get_hardware_address()->get_core() & 0x1) { - // get the first thread on the next core, and abandon if we've - // filled up the hardware. iterator->next_core(); - pTh = iterator->get_thread(); - if (iterator->has_wrapped()) - { - // out of room. Abandon placement. - par->Post(163, pT->Name()); - return true; - } } } } @@ -145,9 +132,9 @@ return false; void Placement::Xlink(P_device * pDe,P_thread * pTh) // Actually link a real device to a real thread { -printf("XLinking device %s with id %d to thread %s\n", +DebugPrint("XLinking device %s with id %d to thread %s\n", pDe->FullName().c_str(), pDe->addr.A_device, pTh->FullName().c_str()); -fflush(stdout); +// fflush(stdout); pDe->pP_thread = pTh; // Device to thread pTh->P_devicel.push_back(pDe); // Thread to device diff --git a/Source/OrchBase/build_defs.cpp b/Source/OrchBase/build_defs.cpp index ba5cc043..b2b5924e 100644 --- a/Source/OrchBase/build_defs.cpp +++ b/Source/OrchBase/build_defs.cpp @@ -37,6 +37,6 @@ const string TINSEL_SRC_PATH = STATIC_SRC_PATH+TINSEL_PATH; const string ORCH_SRC_PATH = STATIC_SRC_PATH+ORCH_PATH; const string BIN_PATH = "bin"; const string BUILD_PATH = "Build"; -const string COREMAKE_BASE = "make all 2>&1 >> make_errs.txt"; +const string COREMAKE_BASE = "make -j$(nproc --ignore=4) all 2>&1 >> make_errs.txt"; const string COREBIN_BASE = "softswitch_"; const void* DRAM_BASE = 0; // temporary: this should be set to the actual DRAM bottom address. diff --git a/Source/OrchBase/build_defs.h b/Source/OrchBase/build_defs.h index 0e94b18e..2416ced4 100644 --- a/Source/OrchBase/build_defs.h +++ b/Source/OrchBase/build_defs.h @@ -2,8 +2,7 @@ #define __BUILD_DEFS_H__ #include -#include "config.h" -//#include "tinsel-config.h" +#include "tinsel-config.h" extern const std::string SYS_COPY; extern const std::string RECURSIVE_CPY; diff --git a/Source/OrchBase/tinsel-config.h b/Source/OrchBase/tinsel-config.h index 10860c56..b7239ad7 100644 --- a/Source/OrchBase/tinsel-config.h +++ b/Source/OrchBase/tinsel-config.h @@ -1,109 +1,5 @@ #ifndef _TINSEL_CONFIG_H_ #define _TINSEL_CONFIG_H_ -#define TinselFPDivLatency 14 -#define TinselCoresPerFPU 4 -#define TinselWordsPerSRAMBeat 2 -#define TinselLogBytesPerDRAM 31 -#define TinselLogCoresPerBoard 6 -#define TinselWordsPerBeat 8 -#define TinselLogReliableLinkRecvBufferSize 9 -#define TinselLogDRAMsPerBoard 1 -#define TinselLogTransmitBufferLen 2 -#define TinselFPConvertLatency 6 -#define TinselBytesPerDRAM 2147483648 -#define TinselFPCompareLatency 3 -#define TinselFPAddSubLatency 14 -#define TinselLogBytesPerFlit 4 -#define TinselLogEastWestLinks 0 -#define TinselNumEastWestLinks 1 -#define TinselMailboxMeshXBits 2 -#define TinselBytesPerSRAMBeat 8 -#define TinselMailboxMeshXLen 4 -#define TinselLinkTimeout 1024 -#define TinselLogFlitsPerThread 6 -#define TinselLogMaxFlitsPerMsg 2 -#define TinselIntMultLatency 3 -#define TinselLogBytesPerMsg 6 -#define TinselDCacheLogNumWays 3 -#define TinselLogNorthSouthLinks 0 -#define TinselDCachesPerDRAM 8 -#define TinselFPUsPerBoard 16 -#define TinselLogThreadsPerDRAM 9 -#define TinselMailboxMeshYBits 2 -#define TinselLogThreadsPerMailbox 6 -#define TinselLogLinesPerDRAM 26 -#define TinselMeshYLen 2 -#define TinselMeshYBits 2 -#define TinselLogWordsPerMsg 4 -#define TinselLogCoresPerFPU 2 -#define TinselSRAMLatency 8 -#define TinselLogBeatsPerDRAM 26 -#define TinselDRAMBase 25165824 -#define TinselDeviceFamily '"Stratix V"' -#define TinselSRAMLogMaxInFlight 5 -#define TinselLogLinesPerMem 27 -#define TinselMaxFlitsPerMsg 4 -#define TinselBeatWidth 256 -#define TinselLogCoresPerMailbox 2 -#define TinselLogOffChipRAMBaseAddr 11 -#define TinselBeatsPerLine 1 -#define TinselLogBytesPerSRAMPartition 15 -#define TinselSharedInstrMem true -#define TinselWordsPerLine 8 -#define TinselLogBytesPerDRAMPartition 21 -#define TinselNumNorthSouthLinks 1 -#define TinselBytesPerBeat 32 -#define TinselMaxBootImageBytes 576 -#define TinselLogCoresPerDCache 2 -#define TinselThreadsPerCore 16 -#define TinselBitsPerLine 256 -#define TinselThreadsPerBoard 1024 -#define TinselFPUOpMaxLatency 14 -#define TinselMacLatency 100 -#define TinselSRAMStoreLatency 2 -#define TinselLogBeatsPerLine 0 -#define TinselLogFPUsPerBoard 4 -#define TinselMeshXLen 3 -#define TinselMaxThreads 16384 -#define TinselLogMsgsPerThread 4 -#define TinselLogThreadsPerCore 4 -#define TinselMailboxMeshYLen 4 -#define TinselFPMultLatency 11 -#define TinselDRAMLatency 20 -#define TinselLogThreadsPerBoard 10 -#define TinselBeatBurstWidth 3 -#define TinselLogInstrsPerCore 11 -#define TinselDCacheLogSetsPerThread 2 -#define TinselLogDCachesPerDRAM 3 -#define TinselLogThreadsPerSRAM 8 -#define TinselMailboxesPerBoard 16 -#define TinselLogWordsPerFlit 2 -#define TinselLogTransmitBufferSize 10 -#define TinselDRAMLogMaxInFlight 5 -#define TinselTransmitBound 20 -#define TinselDRAMsPerBoard 2 -#define TinselThreadsPerDRAM 512 -#define TinselSRAMBurstWidth 3 -#define TinselLogBytesPerLine 5 -#define TinselCoresPerBoard 64 -#define TinselLogMailboxesPerBoard 4 -#define TinselSRAMsPerBoard 4 -#define TinselLogBytesPerSRAM 23 -#define TinselLogBytesPerBeat 5 -#define TinselTargetBoard '"DE5"' -#define TinselSRAMAddrWidth 20 -#define TinselLogWordsPerBeat 3 -#define TinselSRAMDataWidth 64 -#define TinselLogMacRecvBufferSize 5 -#define TinselLogWordsPerLine 3 -#define TinselLogBeatsPerMem 27 -#define TinselLogBytesPerMem 32 -#define TinselWordsPerFlit 4 -#define TinselCoresPerMailbox 4 -#define TinselLogBytesPerSRAMBeat 3 -#define TinselDRAMGlobalsLength 1048576000 -#define TinselCoresPerDCache 4 -#define TinselMeshXBits 2 -#define TinselLogBeatsPerSRAM 18 -#define TinselLogThreadsPerFPU 6 +#define True true // Tinsel config.h uses True instead of true +#include "config.h" #endif diff --git a/Source/OrchestratorMessages.txt b/Source/OrchestratorMessages.txt index e885246d..2399efd9 100644 --- a/Source/OrchestratorMessages.txt +++ b/Source/OrchestratorMessages.txt @@ -72,19 +72,21 @@ 161(W) : "Owner %s does not exist" 162(E) : "No NameServer is running - cannot upload task" 163(E) : "Task %s too large for system - cannot place" -164(E) : "No supervisor found for task %s - cannot map" -165(E) : "Device %s has no or an invalid device type" -166(E) : "Supervisor device %s has no or an invalid device type" +164(E) : "Task %s is placed on %s boxes, but there are only %s mothership processes. Either the hardware model is wrong, or a mothership has died." +165(E) : "System command '%s' ran unsuccessfully. Aborting deployment." +166(E) : "Not enough mothership processes exist to deploy task %s. Is a mothership running?" +167(E) : "System command '%s' ran unsuccessfully with message '%s'. Aborting deployment." 508(U) : "Task %s in an unrecognised state. Reboot the mothership" 509(W) : "Task %s could not be loaded because task %s was already active in state %s" 510(W) : "Unrecognised %s command to mothership %s" 511(W) : "Task %s could not be %s: state is %s" +512(U) : "Task %s could not be %s: state was stuck at %s. Aborting." 513(U) : "Task %s in state %s, never reached barrier. A boot thread may have crashed" 515(W) : "Task %s not mapped to this mothership (%s)" 516(E) : "Boot thread could not be created for board %s for task %s" 517(W) : "Task %s could not be stopped: state is %s" -518(E) : "Unable to boot task completely: %s cores loaded, %s cores expected - aborting" +518(E) : "Unable to boot task %s completely: %s cores loaded, %s cores expected - aborting" 520(W) : "Hardware command ||%s|| to mothership %s failed" 520(W) : "Unrecognised Mothership system command %s" 530(E) : "Tinsel packet not received successfully on mothership %s" @@ -93,6 +95,9 @@ 533(E) : "Bad symbol %s in the Supervisor library on Mothership %s: check XML definition. Error was %s" 534(E) : "Supervisor library could not be successfully unloaded on Mothership %s. Future attempts to run tasks may have strange I/O behaviour" 540(I) : "Application Supervisor being started on Mothership %s" +550(E) : "Unexpected name services state configuration received on Mothership %s. Asked for state %s, actual state is %s" +560(I) : "Task %s booting..." +561(I) : "Task %s ready to be run" 600(I) : "%s" 601(I) : "Core %s: (part %s): %s" @@ -141,8 +146,8 @@ 803(F) : "P_builder:out of memory to create a QCoreApplication object" 804(F) : "P_builder:out of memory to create the graph parser. Shutting down" 805(E) : "P_builder:compile failed. Errors dumped to make_errs.txt" -806(W) : "P_builder:unable to read binary %s. Application may not be completely loaded" -807(E) : "Unable to copy files to directory %s. Check directory exists and has access rights" +806(W) : "P_builder:unable to read binary %s. Application may not be completely loaded. %s" +807(E) : "Unable to copy files to directory %s. Check directory exists and has access rights. %s" 808(W) : "Invalid or nonexistent definition file %s for task %s. Task not loaded" 809(W) : "Tasks from specification %s already loaded - command ignored" 810(E) : "DeviceType %s too big: requires %s bytes but max capacity is %s" @@ -151,6 +156,11 @@ 813(E) : "Error: task %s could not be %s. State on Mothership is %s" 814(W) : "P_builder: no task found with source file %s" 815(W) : "P_builder: task %s not registered under source file %s" +816(E) : "P_Builder: Cannot open %s for writing: %s" +817(E) : "Unable to remove directory %s: %s" +818(E) : "Unable to create directory %s: %s" + + 901(U) : "%s: %s corrupt ?" 902(U) : "OrchBase::ClearTasks(tt=%s)" 903(U) : "OrchBase::ClearDcls: Null pointer to %s in P_typdclm" diff --git a/Source/Parser/poetsdatatype.h b/Source/Parser/poetsdatatype.h index fe8bdf0b..88d5a2a4 100644 --- a/Source/Parser/poetsdatatype.h +++ b/Source/Parser/poetsdatatype.h @@ -3,6 +3,7 @@ #include #include +#include "OSFixes.hpp" class PoetsDataType : public QObject, public QVariant { diff --git a/Source/Parser/psupervisordevicetype.cpp b/Source/Parser/psupervisordevicetype.cpp index d49dccde..f5c88415 100644 --- a/Source/Parser/psupervisordevicetype.cpp +++ b/Source/Parser/psupervisordevicetype.cpp @@ -5,7 +5,7 @@ PSupervisorDeviceType::PSupervisorDeviceType(const QString& name, PIGraphObject *parent) : PIGraphBranch(name, "SupervisorDeviceType", QVector({INPIN, OUTPIN, CODE}), parent), - dev_info_persistent(false), dev_props_valid(false), edge_endpts_exist(false) + dev_info_persistent(false), dev_props_valid(false), edge_endpts_exist(false), device_type(0) { } diff --git a/Source/RTCL/RTCL.cpp b/Source/RTCL/RTCL.cpp index e4e9a619..881e2cb6 100644 --- a/Source/RTCL/RTCL.cpp +++ b/Source/RTCL/RTCL.cpp @@ -8,6 +8,8 @@ #include "Pglobals.h" #include "Cli.h" +#include "OSFixes.hpp" + //------------------------------------------------------------------------------ void * rtcl_func(void * args) @@ -94,10 +96,22 @@ WALKVECTOR(FnMap_t*,FnMapx,F) fprintf(fp,"Function table for comm %d:\n", cIdx++); fprintf(fp,"Key Method\n"); WALKMAP(unsigned,pMeth,(**F),i) - fprintf(fp,"%#010x %#016x\n",(*i).first,(*i).second); +{ + //fprintf(fp,"%#010x 0x%#016x\n",(*i).first,(*i).second); + fprintf(fp,"%#010x ",(*i).first); + + // Now for a horrible double type cast to get us a sensible function pointer. + // void*s are only meant to point to objects, not functions. So we get to a + // void** as a pointer to a function pointer is an object pointer. We can then + // follow this pointer to get to the void*, which we then reinterpret to get + // the function's address as a uint64_t. + fprintf(fp,"%" PTR_FMT "\n",reinterpret_cast( + *(reinterpret_cast(&((*i).second)))) + ); +} } fprintf(fp,"Communication pool:\n"); -fprintf(fp,"pthis : %p\n",comms.pthis); +fprintf(fp,"pthis : %p\n",static_cast(comms.pthis)); fprintf(fp,"tick : %e\n",comms.tick); fprintf(fp,"l_stop : %c\n",comms.l_stop ? 'T' : 'F'); fprintf(fp,"t_stop : %e\n",comms.t_stop); diff --git a/Source/RTCL/RTCLMain.cpp b/Source/RTCL/RTCLMain.cpp index 63b7a50b..a6fea70f 100644 --- a/Source/RTCL/RTCLMain.cpp +++ b/Source/RTCL/RTCLMain.cpp @@ -13,11 +13,11 @@ RTCL * pRTCL = 0; try { pRTCL = new RTCL(argc,argv,string(csRTCLproc)); } -catch(bad_alloc) { +catch(bad_alloc&) { printf("\n\n%s Main out of memory... \n\n",csRTCLproc); fflush(stdout); } -catch(Unrec_t u) { +catch(Unrec_t& u) { u.Post(); } catch(...) { diff --git a/Source/Root/Root.cpp b/Source/Root/Root.cpp index eb6bfb4d..b9c16070 100644 --- a/Source/Root/Root.cpp +++ b/Source/Root/Root.cpp @@ -9,6 +9,8 @@ #include "Cli.h" #include "flat.h" +#include "OSFixes.hpp" + const char * Root::prompt = "POETS>"; //------------------------------------------------------------------------------ @@ -55,8 +57,8 @@ return NULL; //============================================================================== -Root::Root(int argc,char * argv[],string d) : - OrchBase(argc,argv,d,string(__FILE__)) +Root::Root(int argc,char * argv[],string d) + :OrchBase(argc,argv,d,string(__FILE__)) { echo = false; // Batch subsystem opaque injData.flag = 0; // Clear injector controls @@ -74,6 +76,56 @@ if(pthread_create(&kb_thread,NULL,kb_func,args)) fprintf(stdout,"Error creating kb_thread\n"); fflush(stdout); +/* Handle input arguments - grab the hdfPath and/or batchPath. */ +std::string rawArgs; +std::string hdfPath; +std::string batchPath; +for (int i=1; iCl; + if (key=="batch") batchPath = i->GetP(0); + if (key=="hdf") hdfPath = i->GetP(0); + } +} + +/* Queue batch message, if one was given to us. We do this by staging a Cli + * entry using the batch system (see Root::OnIdle). */ +if (!batchPath.empty()) +{ + Equeue.push_front(Cli(dformat("call /file = \"%s\"", batchPath.c_str()))); +} + +/* Pass hardware description file to topology generation, if one was given to + * us. We do this by staging a Cli entry using the batch system (see + * Root::OnIdle). + * + * The reason we do this (as opposed to simply calling TopoLoad) is because we + * haven't built the process map yet - as a consequence, we do not know the + * rank of the LogServer processes, and so can't Post in the event of an error. + * By staging the Cli entry onto the front of the batch queue, we can be sure + * that MPISpinner will drain the input buffer before running our + * command. Since that input buffer will contain messages that will register + * all processes into our pPmap, we know we will have the logserver rank, + * making Post work correctly. */ +if (!hdfPath.empty()) +{ + Equeue.push_front(Cli(dformat("topo /load = \"%s\"", hdfPath.c_str()))); +} + MPISpinner(); // Spin on *all* messages; exit on DIE printf("********* Root rank %d on the way out\n",Urank); fflush(stdout); } @@ -214,9 +266,11 @@ if ((tpL = pPmap[cIdx]->U.LogServer) != Q::NAP) // is this the LogServer's local lIdx = cIdx; // and comm index for(p=0;pM[p],int2str(p)); - Pkt.Send(p); + if ((((static_cast(cIdx) != RootCIdx()) + || (p!=static_cast(Urank)))) && (p!=tpL)) + { // NOT the LogServer + Post(50,pPmap[cIdx]->M[p],int2str(p)); + Pkt.Send(p); } } } @@ -224,7 +278,9 @@ else { for(p=0;p(cIdx) != RootCIdx()) + || (p!=static_cast(Urank)))) + { // Don't need to send to self Post(50,pPmap[cIdx]->M[p],int2str(p)); Pkt.Send(p); } @@ -391,7 +447,19 @@ fprintf(fp,"Key Method\n"); // means it would try to dereference the iterator obtained from // F. Not what is expected... WALKMAP(unsigned,pMeth,(**F),i) - fprintf(fp,"%#010x 0x%#016x\n",(*i).first,(*i).second); +{ + //fprintf(fp,"%#010x 0x%#016x\n",(*i).first,(*i).second); + fprintf(fp,"%#010x ",(*i).first); + + // Now for a horrible double type cast to get us a sensible function pointer. + // void*s are only meant to point to objects, not functions. So we get to a + // void** as a pointer to a function pointer is an object pointer. We can then + // follow this pointer to get to the void*, which we then reinterpret to get + // the function's address as a uint64_t. + fprintf(fp,"%" PTR_FMT "\n",reinterpret_cast( + *(reinterpret_cast(&((*i).second)))) + ); +} } fprintf(fp,"prompt = %s\n",prompt); @@ -548,7 +616,7 @@ if (Cl.Pa_v.size()==0) { Post(47,"conn","system","1"); // need to be given a service to connect to return; } -if (pPmap[0]->vPmap.size() != Usize[0]) +if (static_cast(pPmap[0]->vPmap.size()) != Usize[0]) { Post(62); // can't connect to another universe before we know the size of our own. } diff --git a/Source/Root/Root.h b/Source/Root/Root.h index dac37533..22ec40e3 100644 --- a/Source/Root/Root.h +++ b/Source/Root/Root.h @@ -4,6 +4,7 @@ #include "CommonBase.h" #include "OrchBase.h" #include "PMsg_p.hpp" +#include "RootArgs.h" #include "Cli.h" #include "Injector.h" #include @@ -64,7 +65,3 @@ struct injData_t { //============================================================================== #endif - - - - diff --git a/Source/Root/RootArgs.h b/Source/Root/RootArgs.h new file mode 100644 index 00000000..d8abb4f2 --- /dev/null +++ b/Source/Root/RootArgs.h @@ -0,0 +1,6 @@ +#ifndef __ORCHESTRATOR_SOURCE_ROOT_ROOTARGS_H +#define __ORCHESTRATOR_SOURCE_ROOT_ROOTARGS_H +/* Defines arguments for the root process. */ +#define ROOT_ARG_BATCH "batch" +#define ROOT_ARG_HDF "hdf" +#endif diff --git a/Source/Root/RootMain.cpp b/Source/Root/RootMain.cpp index 66c14673..87984dcc 100644 --- a/Source/Root/RootMain.cpp +++ b/Source/Root/RootMain.cpp @@ -18,11 +18,11 @@ Root * pRoot = 0; try { pRoot = new Root(argc,argv,string(csROOTproc)); } -catch(bad_alloc) { +catch(bad_alloc&) { printf("\n\n%s Main out of memory... \n\n",csROOTproc); fflush(stdout); } -catch(Unrec_t u) { +catch(Unrec_t& u) { u.Post(); } catch(...) { @@ -40,4 +40,3 @@ return 0; } //------------------------------------------------------------------------------ - diff --git a/Source/Softswitch/Makefile b/Source/Softswitch/Makefile index 82e544b7..48886fed 100644 --- a/Source/Softswitch/Makefile +++ b/Source/Softswitch/Makefile @@ -15,6 +15,7 @@ SOFTSWSRC = $(realpath $(SOFTSW_ROOT)/src) PCOMMONINC = $(realpath $(ORCH_ROOT)/Source/Common) GENERICSINC = $(realpath $(ORCH_ROOT)/Generics) +NAMESRVINC = $(realpath $(ORCH_ROOT)/Source/NameServer) ifndef RISCV_PATH export RISCV_PATH := /usr/local/riscv @@ -81,14 +82,15 @@ vpath mpi%.h $(MPICHINC) vpath opa_%.h $(MPICHINC) vpath PMsg_p.hpp $(PCOMMONINC) vpath OSFixes.hpp $(PCOMMONINC) +vpath ABDefs.hpp $(NAMESRVINC) vpath Supervisor.% $(realpath ../$(DEFAULT_GEN_DIR)) -VPATH := $(SOFTSWSRC):$(SOFTSWINC):$(TINSELLIB):$(TINSELINC):$(GENERICSINC) +VPATH := $(SOFTSWSRC):$(SOFTSWINC):$(TINSELLIB):$(TINSELINC):$(GENERICSINC):$(NAMESRVINC) # Local compiler flags CFLAGS = $(RV_CFLAGS) $(LH_CFLAGS) -O2 -I $(TINSELINC) -I $(SOFTSWINC) -I $(GENINC) -I $(PCOMMONINC) LDFLAGS = -melf32lriscv -G 0 -L$(RV_LIBS) -L$(RV_GCC_LIBS) -lgcc -lc -MPICFLAGS = -I$(SOFTSWINC) -I$(TINSELINC) -I$(MPICHINC) -I$(PCOMMONINC) -I$(GENERICSINC) -std=c++11 -fPIC -pipe -Wall +MPICFLAGS = -I$(SOFTSWINC) -I$(TINSELINC) -I$(MPICHINC) -I$(PCOMMONINC) -I$(GENERICSINC) -I$(NAMESRVINC) -std=c++11 -fPIC -pipe -Wall MPILDFLAGS = -shared -L$(MPICHLIB) SECEXPCPPS := $(GENSRC)/%.cpp diff --git a/Source/Softswitch/inc/poets_hardware.h b/Source/Softswitch/inc/poets_hardware.h index de57c324..525cbee5 100644 --- a/Source/Softswitch/inc/poets_hardware.h +++ b/Source/Softswitch/inc/poets_hardware.h @@ -4,9 +4,9 @@ #include #define P_MSG_MAX_SIZE (0x1 << TinselLogBytesPerMsg) -#define LOG_BOARDS_PER_BOX (TinselMeshXBits+TinselMeshYBits) +#define LOG_BOARDS_PER_BOX (TinselMeshXBitsWithinBox+TinselMeshYBitsWithinBox) #define LOG_CORES_PER_BOARD TinselLogCoresPerBoard #define LOG_THREADS_PER_CORE TinselLogThreadsPerCore -#define NUM_BOARDS_PER_BOX (TinselMeshXLen*TinselMeshYLen) +#define NUM_BOARDS_PER_BOX (TinselMeshXLenWithinBox*TinselMeshYLenWithinBox) #endif diff --git a/Source/Softswitch/inc/poets_msg.h b/Source/Softswitch/inc/poets_msg.h index 673d613f..26fdf6a1 100644 --- a/Source/Softswitch/inc/poets_msg.h +++ b/Source/Softswitch/inc/poets_msg.h @@ -17,6 +17,7 @@ #define P_DEVICE_OS 0 #define P_THREAD_OS (LOG_DEVICES_PER_THREAD) #define P_CORE_OS (LOG_THREADS_PER_CORE+P_THREAD_OS) +#define P_MAILBOX_OS (TinselLogCoresPerMailbox+P_CORE_OS) #define P_BOARD_OS (LOG_CORES_PER_BOARD+P_CORE_OS) #define P_BOX_OS (LOG_BOARDS_PER_BOX+P_BOARD_OS) #define P_SUP_OS 31 @@ -28,6 +29,7 @@ #define P_DEVICE_MASK ((0x1 << P_THREAD_OS) - (0x1 << P_DEVICE_OS)) #define P_THREAD_HWOS 0 #define P_CORE_HWOS (LOG_THREADS_PER_CORE+P_THREAD_HWOS) +#define P_MAILBOX_HWOS (TinselLogCoresPerMailbox+P_CORE_HWOS) #define P_BOARD_HWOS (LOG_CORES_PER_BOARD+P_CORE_HWOS) #define P_BOX_HWOS (LOG_BOARDS_PER_BOX+P_BOARD_HWOS) #define P_BOX_HWMASK ((0x1 << P_SUP_OS) - (0x1 << P_BOX_HWOS)) From 76f0fd410c4368c43dbbef866de9eb365937e244 Mon Sep 17 00:00:00 2001 From: AlexRast Date: Thu, 26 Sep 2019 00:41:27 +0100 Subject: [PATCH 07/14] Some extra messaging output during loading --- Build/gcc/Makefile | 5 +- Build/gcc/Makefile.dependencies | 27 +- Build/gcc/Makefile.executable_prerequisites | 68 +- Build/gcc/Resources/orchestrate_template.sh | 159 ++--- Source/Common/CommonBase.h | 1 - Source/HostLink/DebugLink.cpp | 105 --- Source/HostLink/DebugLink.h | 50 -- Source/HostLink/HostLink.cpp | 611 ------------------ Source/HostLink/HostLink.h | 139 ---- Source/HostLink/JtagAtlantic.h | 75 --- Source/HostLink/Makefile | 42 -- Source/HostLink/MemFileReader.cpp | 73 --- Source/HostLink/MemFileReader.h | 26 - Source/HostLink/Monitor/Monitor.cpp | 70 -- Source/HostLink/Monitor/Monitor.h | 33 - Source/HostLink/Monitor/Monitor.~cpp | 70 -- Source/HostLink/Monitor/MonitorMain.cpp | 34 - Source/HostLink/Monitor/PowerLink.cpp | 246 ------- Source/HostLink/PowerLink.cpp | 246 ------- Source/HostLink/PowerLink.h | 22 - Source/HostLink/UART.cpp | 188 ------ Source/HostLink/UART.h | 40 -- Source/HostLink/driver/Makefile | 10 - Source/HostLink/driver/dmabuffer.c | 249 ------- Source/HostLink/pciestreamd.cpp | 445 ------------- Source/HostLink/tinsel-power.cpp | 27 - Source/HostLink/udsock.c | 114 ---- Source/Mothership/Mothership.cpp | 542 +++++++++++----- Source/Mothership/Mothership.h | 35 +- Source/Mothership/MothershipMain.cpp | 2 + Source/NameServer/NameServer.cpp | 4 +- .../HardwareConfigurationDeployment/README.md | 12 +- Source/OrchestratorMessages.txt | 43 +- Source/Parser/pdeviceinstance.cpp | 2 + Source/Parser/pdeviceinstance.h | 2 +- Source/Parser/pdevicetype.cpp | 8 +- Source/Parser/pdevicetype.h | 2 +- Source/Parser/pedgeinstance.cpp | 4 +- Source/Parser/pedgeinstance.h | 2 +- Source/Parser/pidatatype.cpp | 9 +- Source/Parser/pidatatype.h | 2 +- Source/Parser/pigraphinstance.cpp | 4 +- Source/Parser/pigraphinstance.h | 2 +- Source/Parser/pigraphroot.cpp | 7 +- Source/Parser/pigraphroot.h | 2 +- Source/Parser/pigraphtype.cpp | 4 +- Source/Parser/pigraphtype.h | 2 +- Source/Parser/piinputpin.cpp | 3 +- Source/Parser/piinputpin.h | 2 +- Source/Parser/pioutputpin.cpp | 2 +- Source/Parser/pioutputpin.h | 2 +- Source/Parser/pmessagetype.cpp | 2 +- Source/Parser/pmessagetype.h | 2 +- Source/Parser/poetsdatatype.cpp | 47 ++ Source/Parser/poetsdatatype.h | 10 +- Source/Parser/psupervisordevicetype.cpp | 4 +- Source/Parser/psupervisordevicetype.h | 2 +- Source/Softswitch/Makefile | 6 +- Source/Softswitch/inc/poets_msg.h | 14 +- .../{ => Dialect1}/aesop_dialect_1.uif | 0 .../duplicate_section_invalid.uif | 0 .../{ => Dialect1}/invalid_section.uif | 0 .../missing_section_invalid.uif | 0 .../syntactically_invalid_test_file.uif | 0 .../{ => Dialect1}/valid_test_file.uif | 0 .../Dialect3/Invalid/README.txt | 4 + ...invalid_dialect_3_board_name_too_short.uif | 129 ++++ .../invalid_dialect_3_box_name_too_long.uif | 129 ++++ ...lid_dialect_3_broken_header_variable_1.uif | 126 ++++ ...lid_dialect_3_broken_header_variable_2.uif | 126 ++++ .../invalid_dialect_3_duplicate_section.uif | 136 ++++ .../Invalid/invalid_dialect_3_empty.uif | 0 .../invalid_dialect_3_floating_dram.uif | 129 ++++ ...id_dialect_3_invalid_character_in_type.uif | 129 ++++ .../invalid_dialect_3_missing_board_type.uif | 128 ++++ .../invalid_dialect_3_missing_box_type.uif | 128 ++++ .../invalid_dialect_3_missing_cost.uif | 128 ++++ ...invalid_dialect_3_missing_mailbox_type.uif | 128 ++++ ...ialect_3_missing_mandatory_header_item.uif | 128 ++++ ..._3_missing_mandatory_header_variable_1.uif | 126 ++++ ..._3_missing_mandatory_header_variable_2.uif | 126 ++++ ...nvalid_dialect_3_missing_packet_length.uif | 128 ++++ ...lid_dialect_3_missing_referenced_board.uif | 128 ++++ ...d_dialect_3_missing_referenced_board_2.uif | 129 ++++ ...valid_dialect_3_missing_referenced_box.uif | 128 ++++ ...d_dialect_3_missing_referenced_mailbox.uif | 128 ++++ ...dialect_3_missing_referenced_mailbox_2.uif | 129 ++++ ..._missing_reverse_edge_board_definition.uif | 129 ++++ ...issing_reverse_edge_mailbox_definition.uif | 129 ++++ .../invalid_dialect_3_missing_section.uif | 121 ++++ .../invalid_dialect_3_nonfloat_cost.uif | 129 ++++ .../invalid_dialect_3_type_too_long.uif | 129 ++++ .../invalid_dialect_3_type_too_short.uif | 129 ++++ .../invalid_dialect_3_undefined_type.uif | 129 ++++ .../StaticResources/Dialect3/Valid/8_box.uif | 193 ++++++ .../StaticResources/Dialect3/Valid/README.txt | 4 + .../Dialect3/Valid/valid_dialect_3.uif | 129 ++++ .../Valid/valid_dialect_3_mismatched_name.uif | 129 ++++ ...valid_dialect_3_some_types_in_sections.uif | 129 ++++ .../valid_dialect_3_types_everywhere.uif | 128 ++++ ...lid_dialect_3_bad_dialect_definition_1.uif | 129 ++++ ...lid_dialect_3_bad_dialect_definition_2.uif | 128 ++++ ...lid_dialect_3_bad_dialect_definition_3.uif | 129 ++++ ...lid_dialect_3_bad_dialect_definition_4.uif | 129 ++++ Tests/TestDialect1Reader.cpp | 46 ++ Tests/TestDialect3Reader.cpp | 112 ++++ Tests/TestHardwareAddressAndFormat.cpp | 41 +- Tests/TestHardwareFileParser.cpp | 92 --- Tests/TestHardwareFileReader.cpp | 87 +++ 109 files changed, 5608 insertions(+), 3423 deletions(-) mode change 100755 => 100644 Build/gcc/Makefile.executable_prerequisites delete mode 100755 Source/HostLink/DebugLink.cpp delete mode 100755 Source/HostLink/DebugLink.h delete mode 100755 Source/HostLink/HostLink.cpp delete mode 100755 Source/HostLink/HostLink.h delete mode 100755 Source/HostLink/JtagAtlantic.h delete mode 100755 Source/HostLink/Makefile delete mode 100755 Source/HostLink/MemFileReader.cpp delete mode 100755 Source/HostLink/MemFileReader.h delete mode 100755 Source/HostLink/Monitor/Monitor.cpp delete mode 100755 Source/HostLink/Monitor/Monitor.h delete mode 100755 Source/HostLink/Monitor/Monitor.~cpp delete mode 100755 Source/HostLink/Monitor/MonitorMain.cpp delete mode 100755 Source/HostLink/Monitor/PowerLink.cpp delete mode 100755 Source/HostLink/PowerLink.cpp delete mode 100755 Source/HostLink/PowerLink.h delete mode 100755 Source/HostLink/UART.cpp delete mode 100755 Source/HostLink/UART.h delete mode 100755 Source/HostLink/driver/Makefile delete mode 100755 Source/HostLink/driver/dmabuffer.c delete mode 100755 Source/HostLink/pciestreamd.cpp delete mode 100755 Source/HostLink/tinsel-power.cpp delete mode 100755 Source/HostLink/udsock.c rename Tests/StaticResources/{ => Dialect1}/aesop_dialect_1.uif (100%) rename Tests/StaticResources/{ => Dialect1}/duplicate_section_invalid.uif (100%) rename Tests/StaticResources/{ => Dialect1}/invalid_section.uif (100%) rename Tests/StaticResources/{ => Dialect1}/missing_section_invalid.uif (100%) rename Tests/StaticResources/{ => Dialect1}/syntactically_invalid_test_file.uif (100%) rename Tests/StaticResources/{ => Dialect1}/valid_test_file.uif (100%) create mode 100644 Tests/StaticResources/Dialect3/Invalid/README.txt create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_board_name_too_short.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_box_name_too_long.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_broken_header_variable_1.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_broken_header_variable_2.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_duplicate_section.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_empty.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_floating_dram.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_invalid_character_in_type.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_board_type.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_box_type.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_cost.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mailbox_type.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mandatory_header_item.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mandatory_header_variable_1.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mandatory_header_variable_2.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_packet_length.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_board.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_board_2.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_box.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_mailbox.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_mailbox_2.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_reverse_edge_board_definition.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_reverse_edge_mailbox_definition.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_section.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_nonfloat_cost.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_type_too_long.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_type_too_short.uif create mode 100644 Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_undefined_type.uif create mode 100644 Tests/StaticResources/Dialect3/Valid/8_box.uif create mode 100644 Tests/StaticResources/Dialect3/Valid/README.txt create mode 100644 Tests/StaticResources/Dialect3/Valid/valid_dialect_3.uif create mode 100644 Tests/StaticResources/Dialect3/Valid/valid_dialect_3_mismatched_name.uif create mode 100644 Tests/StaticResources/Dialect3/Valid/valid_dialect_3_some_types_in_sections.uif create mode 100644 Tests/StaticResources/Dialect3/Valid/valid_dialect_3_types_everywhere.uif create mode 100644 Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_1.uif create mode 100644 Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_2.uif create mode 100644 Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_3.uif create mode 100644 Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_4.uif create mode 100644 Tests/TestDialect1Reader.cpp create mode 100644 Tests/TestDialect3Reader.cpp delete mode 100644 Tests/TestHardwareFileParser.cpp create mode 100644 Tests/TestHardwareFileReader.cpp diff --git a/Build/gcc/Makefile b/Build/gcc/Makefile index 109635c2..77dc07ad 100644 --- a/Build/gcc/Makefile +++ b/Build/gcc/Makefile @@ -118,8 +118,9 @@ INJECTOR_EXECUTABLE = $(EXECUTABLE_DIR)/injector MOTHERSHIP_EXECUTABLE = $(EXECUTABLE_DIR)/mothership MOTHERSHIP_DUMMY_EXECUTABLE = $(EXECUTABLE_DIR)/mothershipdummy -all: orchestrate root dummy logserver nameserver rtcl injector mothership \ - mothershipdummy application_staging_environment +all: orchestrate launcher root dummy logserver nameserver rtcl injector \ + mothership mothershipdummy application_staging_environment +launcher: $(LAUNCHER_EXECUTABLE) orchestrate: $(ORCHESTRATE_SCRIPT) root: $(ROOT_EXECUTABLE) dummy: $(DUMMY_EXECUTABLE) diff --git a/Build/gcc/Makefile.dependencies b/Build/gcc/Makefile.dependencies index 1299a2c4..599d3b06 100644 --- a/Build/gcc/Makefile.dependencies +++ b/Build/gcc/Makefile.dependencies @@ -1,21 +1,22 @@ # This Makefile defines the software dependencies required to build the # Orchestrator. # -# As a user, you will need to change the contents of this file to match your -# installation. If you are using the distributed "orchestrator_dependencies" -# tarball, you'll only need to change ORCHESTRATOR_DEPENDENCIES_DIR to point to -# where you have extracted it to. -# -# In Mark's case, it looks like this: -ORCHESTRATOR_DEPENDENCIES_DIR ?= /local/orchestrator-common/orchestrator_dependencies-6 +# As a user, if you are on a Tinsel-POETS box, you do not have to change this +# file. If you're not, and you're using the "orchestrator_dependencies" +# tarball, you'll need to change ORCHESTRATOR_DEPENDENCIES_DIR to point to +# where you have extracted to. On Tinsel-POETS boxes, it should be: +ORCHESTRATOR_DEPENDENCIES_DIR ?= /local/orchestrator-common/orchestrator_dependencies_6 + +# You'll also need to ensure you've checked out this repository with the Tinsel +# submodule, and that you've built the hostlink. Otherwise, you will need to +# configure the following to match your setup... -# If you're using the "orchestrator_dependencies" tarball, you shouldn't need -# to do anything more. If you aren't, you'll need to configure the following to -# match your setup however... +# Path to the root directory of the Orchestrator (not to be confused with the +# root process of the Orchestrator). +ROOT_DIR = ../.. # Tinsel, search in this order, using secondary if none found. -#TINSEL_DEFAULT_PRIMARY = "/local/tinsel" -TINSEL_DEFAULT_PRIMARY = "/home/alex_rast/adr1r17/tinsel/tinsel-tinsel-0.6.1" +TINSEL_DEFAULT_PRIMARY = "/local/tinsel" TINSEL_DEFAULT_SECONDARY = "$(ROOT_DIR)/Tinsel" TINSEL_DIR = $(shell test -d "$(TINSEL_DEFAULT_PRIMARY)" && \ echo "$(TINSEL_DEFAULT_PRIMARY)" || \ @@ -40,7 +41,7 @@ QT_INC_DIR = $(QT_DIR)/include # GCC, not used for building the Orchestrator, but necessary for the # Mothership. Immediate-set, because it's used by the shell. -GCC_LIB_DIR := $(ORCHESTRATOR_DEPENDENCIES_DIR)/gcc-7.3.0/lib64 +GCC_LIB_DIR := $(ORCHESTRATOR_DEPENDENCIES_DIR)/gcc-7.3.0/lib64:/usr/lib64 # libcr CR_LIB_DIR := $(ORCHESTRATOR_DEPENDENCIES_DIR)/libcr diff --git a/Build/gcc/Makefile.executable_prerequisites b/Build/gcc/Makefile.executable_prerequisites old mode 100755 new mode 100644 index b8746102..8fa80e05 --- a/Build/gcc/Makefile.executable_prerequisites +++ b/Build/gcc/Makefile.executable_prerequisites @@ -79,18 +79,6 @@ HARDWARE_FILE_READER_SOURCES = $(SOURCE_DIR)/OrchBase/HardwareFileReader/Hardwar $(GENERICS_DIR)/uif.cpp \ $(HARDWARE_DEPLOYER_SOURCES) -# Sources used by executables that provide POETS name resolution. -NAME_SERVICES_SOURCES = $(SOURCE_DIR)/NameServer/ABDefs.cpp \ - $(SOURCE_DIR)/NameServer/ABRecord.cpp \ - $(SOURCE_DIR)/NameServer/ABTask.cpp \ - $(SOURCE_DIR)/NameServer/AddressBook.cpp \ - $(SOURCE_DIR)/NameServer/SBase.cpp \ - $(SOURCE_DIR)/OrchBase/CMsg_p.cpp \ - $(SOURCE_DIR)/OrchBase/P_addr.cpp - -# The LogServer message file is a static text file -LOGSERVER_MESSAGE_FILE_ORIGIN = $(SOURCE_DIR)/OrchestratorMessages.txt - # The orchestrate execution script uses a template. ORCHESTRATE_TEMPLATE = ./Resources/orchestrate_template.sh @@ -137,12 +125,7 @@ ROOT_SOURCES = $(SOURCE_DIR)/Root/RootMain.cpp \ $(GENERICS_DIR)/filename.cpp \ $(GENERICS_DIR)/lex.cpp \ $(GENERICS_DIR)/rand.cpp \ - $(SOURCE_DIR)/Common/Debug.cpp \ - $(SOURCE_DIR)/NameServer/Ns_el.cpp \ - $(SOURCE_DIR)/NameServer/ABDefs.cpp \ - $(SOURCE_DIR)/NameServer/ABRecord.cpp \ - $(SOURCE_DIR)/NameServer/ABTask.cpp \ - $(SOURCE_DIR)/NameServer/AddressBook.cpp + $(SOURCE_DIR)/NameServer/Ns_el.cpp ROOT_SOURCES += $(TRULY_COMMON_SOURCES) ROOT_SOURCES += $(HARDWARE_FILE_READER_SOURCES) @@ -195,26 +178,6 @@ LOGSERVER_SOURCES += $(TRULY_COMMON_SOURCES) # from which the Orchestrator is started. LOGSERVER_MESSAGE_FILE_ORIGIN = $(SOURCE_DIR)/OrchestratorMessages.txt -# The nameserver component consists of: -# -# - The "main" file (/Source/NameServer/NameServerMain.cpp) and its derived -# class in /Source/NameServer. -# -# - The name services sources in /Source/NameServer. -# -# - The truly common sources. -# -# - The hardware and software address sources. -# -NAMESERVER_SOURCES = $(SOURCE_DIR)/NameServer/NameServerMain.cpp \ - $(SOURCE_DIR)/NameServer/NameServer.cpp \ - $(SOURCE_DIR)/Common/Debug.cpp - -NAMESERVER_SOURCES += $(NAME_SERVICES_SOURCES) -NAMESERVER_SOURCES += $(TRULY_COMMON_SOURCES) -#NAMESERVER_SOURCES += $(SOFTWARE_ADDRESS_SOURCES) -#NAMESERVER_SOURCES += $(HARDWARE_ADDRESS_SOURCES) - # The rtcl (real-time clock) component consists of: # # - The "main" file (/Source/RTCL/RTCL.cpp) and its dependencies in @@ -257,8 +220,6 @@ INJECTOR_SOURCES += $(TRULY_COMMON_SOURCES) # # - An interface source file from the softswitch, in /Source/Softswitch/src. # -# - The name services sources. -# # - The truly common sources. # # - Some "generics" (/Generics) sources. @@ -268,40 +229,25 @@ INJECTOR_SOURCES += $(TRULY_COMMON_SOURCES) # Hostlink sources are to be compiled separately. MOTHERSHIP_SOURCES = $(SOURCE_DIR)/Mothership/MothershipMain.cpp \ $(SOURCE_DIR)/Mothership/TaskInfo.cpp \ - $(SOURCE_DIR)/Mothership/Mothership.cpp \ + $(SOURCE_DIR)/Mothership/TMoth.cpp \ $(SOURCE_DIR)/Softswitch/src/poets_msg.cpp \ $(GENERICS_DIR)/dumpchan.cpp \ $(GENERICS_DIR)/rand.cpp \ - $(SOURCE_DIR)/Common/Debug.cpp \ $(SOURCE_DIR)/OrchBase/Bin.cpp \ $(SOURCE_DIR)/OrchBase/build_defs.cpp \ $(SOURCE_DIR)/OrchBase/CFrag.cpp \ + $(SOURCE_DIR)/OrchBase/CMsg_p.cpp \ $(SOURCE_DIR)/OrchBase/D_graph.cpp \ + $(SOURCE_DIR)/OrchBase/P_addr.cpp \ $(SOURCE_DIR)/OrchBase/P_device.cpp \ $(SOURCE_DIR)/OrchBase/P_message.cpp \ $(SOURCE_DIR)/OrchBase/P_pin.cpp \ $(SOURCE_DIR)/OrchBase/P_task.cpp \ + $(SOURCE_DIR)/Common/Debug.cpp \ $(HARDWARE_DEPLOYER_SOURCES) -MOTHERSHIP_SOURCES += $(NAME_SERVICES_SOURCES) MOTHERSHIP_SOURCES += $(TRULY_COMMON_SOURCES) -# A dummy mothership is an executable to provide mothership-like functionality -# when there is no connected box with a HostLink interface. It consists of: -# -# - The "main" file (/Source/Mothership/MothershipDummyMain.cpp) and its -# dependencies in /Source/Mothership -# -# - The name services sources. -# -# - The truly common sources. -# -MOTHERSHIP_DUMMY_SOURCES = $(SOURCE_DIR)/Mothership/MothershipDummyMain.cpp \ - $(SOURCE_DIR)/Mothership/MothershipDummy.cpp - -MOTHERSHIP_DUMMY_SOURCES += $(NAME_SERVICES_SOURCES) -MOTHERSHIP_DUMMY_SOURCES += $(TRULY_COMMON_SOURCES) - # Convert these lists of sources into a list of objects to define a dependency # system for the linker. @@ -333,7 +279,7 @@ $(1)_OBJECTS := $(call orch_sources_to_objects, $($(1)_SOURCES)) endef $(foreach object_set,\ - LAUNCHER ROOT INJECTOR DUMMY LOGSERVER NAMESERVER RTCL MOTHERSHIP MOTHERSHIP_DUMMY,\ + LAUNCHER ROOT INJECTOR DUMMY LOGSERVER RTCL MOTHERSHIP,\ $(eval $(call OBJECT_TEMPLATE,$(object_set)))) # Define executable prerequisites. RULE_TEMPLATE defines the substitutions made @@ -346,5 +292,5 @@ $($(1)_EXECUTABLE): $($(1)_OBJECTS) endef $(foreach executable_name,\ - LAUNCHER ROOT DUMMY LOGSERVER NAMESERVER RTCL INJECTOR MOTHERSHIP MOTHERSHIP_DUMMY,\ + LAUNCHER ROOT DUMMY LOGSERVER RTCL INJECTOR MOTHERSHIP,\ $(eval $(call RULE_TEMPLATE,$(executable_name)))) diff --git a/Build/gcc/Resources/orchestrate_template.sh b/Build/gcc/Resources/orchestrate_template.sh index 183466d0..492817bc 100755 --- a/Build/gcc/Resources/orchestrate_template.sh +++ b/Build/gcc/Resources/orchestrate_template.sh @@ -1,7 +1,7 @@ #!/bin/bash # This script sets up the (runtime) environment for the Orchestrator to operate -# in, then starts the Orchestrator. +# in, tweaks input arguments, then starts the Orchestrator launcher. # # Note that this script will not work correctly if you move any of the # dependencies, or the build artefacts. In that case, you're on your own (or @@ -12,67 +12,7 @@ # substitute in variable names to create the executable. There should be no # double-curly braces in the output version of this file. # -# Call with the --help argument for usage, or just read this string: - -HELP=\ -"Usage: $(basename "$0") [-h] [-m] N - -Starts the Orchestrator. - -Will only start mothership process(es) if called on a POETS box. - -Optional arguments: - -h, --help: Prints this help text. - -m, --motherships N: Defines the number of mothership processes to spawn. \ -Only works if called on a POETS box. N must be a natural number with no \ -leading zeroes." - -# Parse input arguments to determine mothership behaviour. This clears the -# argument list. -NUMBER_OF_MOTHERSHIPS=1 # By default, start one mothership if running on a - # POETS box. -MOTHERSHIP_ARG_SET=0 -while [ $# -gt 0 ]; do - case "$1" in - --help|-h) - # Write help and exit. - echo "$HELP" - exit 0;; - --motherships|-m) - # Test input argument. - NATURAL_REGEXP='^[0-9][[:digit:]]*$' - if [ -z "$2" ]; then - >&2 echo "No input argument provided for --motherships. Pass \ -a natural number with no leading zeroes." - exit 1 - elif ! [[ "$2" =~ $NATURAL_REGEXP ]]; then - >&2 echo "Invalid input argument for --motherships: \"$2\". \ -It should be a natural number with no leading zeroes." - exit 1 - fi - - NUMBER_OF_MOTHERSHIPS="$2" - MOTHERSHIP_ARG_SET=1 - shift; shift;; - esac -done - -# Determine whether or not we are running on a POETS box. -QUARTUS_SETUP_SCRIPT="/local/ecad/setup-quartus17v0.bash" -if [ -f "$QUARTUS_SETUP_SCRIPT" ]; then - ON_POETS_BOX=1 # True -else - ON_POETS_BOX=0 -fi - -# Warn if the user set the number of motherships, and if we're not running -# on a POETS box. -if [ $MOTHERSHIP_ARG_SET -eq 1 ] && \ - [ $ON_POETS_BOX -eq 0 ] && \ - [ $NUMBER_OF_MOTHERSHIPS -ne 0 ]; then - >&2 echo "WARNING: Mothership argument was provided, but this script was \ -not run on a POETS box. Continuing without spawning any motherships." -fi +# Call with the -h argument for usage. # Setup for building applications export RISCV_PATH="{{ RISCV_DIR }}" @@ -80,7 +20,9 @@ export MPICH_PATH="{{ MPICH_DIR }}" export PATH="{{ MPICH_DIR }}/bin:{{ RISCV_BIN_DIR }}:$PATH" export TRIVIAL_LOG_HANDLER=1 -if [ $ON_POETS_BOX -eq 1 ]; then +# Quartus +QUARTUS_SETUP_SCRIPT="/local/ecad/setup-quartus17v0.bash" +if [ -f "$QUARTUS_SETUP_SCRIPT" ]; then export LM_LICENSE_FILE=:27012@localhost:27001@localhost source "$QUARTUS_SETUP_SCRIPT" fi @@ -90,23 +32,88 @@ MPI_LIB_DIR="{{ MPICH_LIB_DIR }}" QT_LIB_DIR="{{ QT_LIB_DIR }}" GCC_LIB_DIR="{{ GCC_LIB_DIR }}" CR_LIB_DIR="{{ CR_LIB_DIR }}" -INTERNAL_LIB_PATH=./:"$QT_LIB_DIR":"$MPI_LIB_DIR":"$GCC_LIB_DIR": +INTERNAL_LIB_PATH="$QT_LIB_DIR":"$MPI_LIB_DIR":"$GCC_LIB_DIR": # Paths for dynamically-linked libraries required by MPI. -export LD_LIBRARY_PATH="$CR_LIB_DIR":"$LD_LIBRARY_PATH":./ +export LD_LIBRARY_PATH="$CR_LIB_DIR":"$MPI_LIB_DIR":"$LD_LIBRARY_PATH":./ + +# Transform input arguments: +# +# Grab the file and batch (/f, /b) command line arguments, put them in +# suppressed quotes for the Cli class in the launcher, and make them relative +# to this directory if it is itself a relative path (because the executable is +# run from the executable directory). +# +# Also perform the "=" style split for Windows/GNU. Windows options are of the +# form: +# /d +# /f = FILE +# Whereas GNU options are of the form: +# -d +# -f FILE +GNU_HELP=0 +THIS_ARG_MODE="" +while [ $# -gt 0 ]; do + VALUE="" -# Define general MPI execution command. -COMMAND="mpiexec.hydra -genv LD_LIBRARY_PATH \"$INTERNAL_LIB_PATH\" \ - -n 1 ./root : \ - -n 1 ./logserver : \ - -n 1 ./rtcl" + # Get switch and value, if appropriate. + case "$1" in + /*) # Windows + THIS_ARG_MODE="Windows" # i.e. not GNU. + SWITCH="$1" + if [ "$2" == "=" ]; then # e.g. "/f = FILE" + VALUE="$3" + shift; shift; shift + else # e.g. "/d" + shift; + fi + ;; + + -*) # GNU + THIS_ARG_MODE="GNU" + SWITCH="/${1:1}" + case "$2" in + -*) # e.g. "-d" + shift + ;; + *) # e.g. "-f FILE", or "-d" with no further arguments (VALUE + # will remain empty). + VALUE="$2" + shift; shift; + ;; + esac + esac + + # Absolute/relative control flow split for arguments that accept a path. + if [ "$SWITCH" == "/f" ] || [ "$SWITCH" == "/b" ]; then + case "$VALUE" in + /*) # Absolute + ARGS="$ARGS $SWITCH = \"$VALUE\"" + ;; + *) # Relative, or user has not specified one + ARGS="$ARGS $SWITCH = \"$PWD/$VALUE\"" + ;; + esac + + # Offer more extensive help for GNU people and the naive. + elif [ "$SWITCH" == "/h" ] || [ "$SWITCH" == "/-help" ]; then + ARGS="$ARGS /h" + [ "$THIS_ARG_MODE" == "GNU" ] && GNU_HELP=1 + + # Otherwise, concatenate the command normally. + else + if [ -z "$VALUE" ]; then + ARGS="$ARGS $SWITCH" + else + ARGS="$ARGS $SWITCH = $VALUE" + fi + fi +done -# Running the Orchestrator from the build directory. Only start a mothership if -# we are running on a POETS box. +# Run the launcher from the build directory. pushd "{{ EXECUTABLE_DIR }}" > /dev/null -if [ $ON_POETS_BOX -eq 1 ]; then - $COMMAND : -n $NUMBER_OF_MOTHERSHIPS ./mothership -else - $COMMAND +./orchestrate /p = "\"$INTERNAL_LIB_PATH\"" $ARGS +if [ $GNU_HELP -eq 1 ]; then + printf "\nNote that there is limited support for short-form GNU-style switches (e.g. '-h -f FILE').\n" fi popd > /dev/null diff --git a/Source/Common/CommonBase.h b/Source/Common/CommonBase.h index b51cf4ef..e1222545 100644 --- a/Source/Common/CommonBase.h +++ b/Source/Common/CommonBase.h @@ -5,7 +5,6 @@ #include #include using namespace std; -#include "Debug.h" #include "PMsg_p.hpp" #include "ProcMap.h" #include "pthread.h" diff --git a/Source/HostLink/DebugLink.cpp b/Source/HostLink/DebugLink.cpp deleted file mode 100755 index b47b467c..00000000 --- a/Source/HostLink/DebugLink.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include -#include -#include -#include -#include "DebugLink.h" - -// Open UART with given instance id -void DebugLink::open(int instId) -{ - uart.open(instId); -} - -// Send query request -void DebugLink::putQuery() -{ - uint8_t cmd[2]; - cmd[0] = DEBUGLINK_QUERY_IN; - uart.put(cmd, 1); - uart.flush(); -} - -// Get query response -bool DebugLink::getQuery(uint32_t* boardId) -{ - uint8_t cmd[2]; - uart.get(cmd, 2); - if (cmd[0] != DEBUGLINK_QUERY_OUT) { - fprintf(stderr, "Unexpected response to CoreLink query command\n"); - exit(EXIT_FAILURE); - } - if (cmd[1] != 0) { - *boardId = (uint32_t) (cmd[1] - 1); - return true; - } - return false; -} - -// Set destination to thread id -void DebugLink::setDest(uint32_t coreId, uint32_t threadId) -{ - uint8_t cmd[3]; - // SetDest command - cmd[0] = DEBUGLINK_SET_DEST; - // Core-local thread id - cmd[1] = threadId; - // Board-local core id - cmd[2] = coreId; - // Send command - uart.put(cmd, 3); -} - -// Set destinations to given local thread id on every core -void DebugLink::setBroadcastDest(uint32_t threadId) -{ - uint8_t cmd[3]; - // SetDest command - cmd[0] = DEBUGLINK_SET_DEST; - // Core-local thread id - cmd[1] = (uint8_t) threadId; - // Broadcast address - cmd[2] = 0x80; - // Send command - uart.put(cmd, 3); -} - -// Send byte to destination thread (StdIn) -void DebugLink::put(uint8_t byte) -{ - uint8_t cmd[2]; - cmd[0] = DEBUGLINK_STD_IN; - cmd[1] = byte; - uart.put(cmd, 2); -} - -// Receive byte (StdOut) -void DebugLink::get(uint32_t* coreId, uint32_t* threadId, uint8_t* byte) -{ - uint8_t cmd[4]; - uart.get(cmd, 4); - if (cmd[0] != DEBUGLINK_STD_OUT) { - fprintf(stderr, "Got unexpected response. Expected StdOut."); - exit(EXIT_FAILURE); - } - *coreId = (uint32_t) cmd[2]; - *threadId = (uint32_t) cmd[1]; - *byte = cmd[3]; -} - -// Is a data available for reading? -bool DebugLink::canGet() -{ - return uart.canGet(); -} - -// Flush writes -void DebugLink::flush() -{ - uart.flush(); -} - -// Close UART -void DebugLink::close() -{ - uart.close(); -} diff --git a/Source/HostLink/DebugLink.h b/Source/HostLink/DebugLink.h deleted file mode 100755 index bfe362d2..00000000 --- a/Source/HostLink/DebugLink.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef _DEBUGLINK_H_ -#define _DEBUGLINK_H_ - -#include -#include -#include -#include "UART.h" - -// CoreLink commands -#define DEBUGLINK_QUERY_IN 0 -#define DEBUGLINK_QUERY_OUT 0 -#define DEBUGLINK_SET_DEST 1 -#define DEBUGLINK_STD_IN 2 -#define DEBUGLINK_STD_OUT 2 - -class DebugLink { - UART uart; - public: - // Open UART with given instance id - void open(int instId); - - // Put query request - void putQuery(); - - // Get query response, including board id - bool getQuery(uint32_t* boardId); - - // Set destination core and thread - void setDest(uint32_t coreId, uint32_t threadId); - - // Set destinations to core-local thread id on every core - void setBroadcastDest(uint32_t threadId); - - // Send byte to destination thread (StdIn) - void put(uint8_t byte); - - // Receive byte (StdOut) - void get(uint32_t* coreId, uint32_t* threadId, uint8_t* byte); - - // Is a data available for reading? - bool canGet(); - - // Flush writes - void flush(); - - // Close UART - void close(); -}; - -#endif diff --git a/Source/HostLink/HostLink.cpp b/Source/HostLink/HostLink.cpp deleted file mode 100755 index 99064b42..00000000 --- a/Source/HostLink/HostLink.cpp +++ /dev/null @@ -1,611 +0,0 @@ -#include "HostLink.h" -#include "DebugLink.h" -#include "MemFileReader.h" -#include "PowerLink.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Function to connect to a PCIeStream UNIX domain socket -static int connectToPCIeStream(const char* socketPath) -{ - // Create socket - int sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock == -1) { - perror("socket"); - exit(EXIT_FAILURE); - } - - // Make it non-blocking - int opts = fcntl(sock, F_GETFL); - fcntl(sock, F_SETFL, opts | O_NONBLOCK); - - // Connect socket - struct sockaddr_un addr; - memset(&addr, 0, sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - addr.sun_path[0] = '\0'; - strncpy(&addr.sun_path[1], socketPath, sizeof(addr.sun_path) - 2); - int ret = connect(sock, (const struct sockaddr *) &addr, - sizeof(struct sockaddr_un)); - if (ret == -1) { - fprintf(stderr, "Can't connect to PCIeStream daemon.\n" - "Either the daemon is not running or it is " - "being used by another process\n"); - exit(EXIT_FAILURE); - } - - // Make it blocking again - fcntl(sock, F_SETFL, opts); - - return sock; -} - -// Constructor -HostLink::HostLink() -{ - // Open lock file - lockFile = open("/tmp/HostLink.lock", O_CREAT, 0444); - if (lockFile == -1) { - perror("Unable to open HostLink lock file"); - exit(EXIT_FAILURE); - } - - // Acquire lock - if (flock(lockFile, LOCK_EX | LOCK_NB) != 0) { - perror("Failed to acquire HostLink lock"); - exit(EXIT_FAILURE); - } - - // Determine number of boards - int numBoards = TinselMeshXLen * TinselMeshYLen + 1; - - // Power down mesh boards - #ifndef SIMULATE - powerdown(); - sleep(1); - #endif - - // Ignore SIGPIPE - signal(SIGPIPE, SIG_IGN); - - #ifdef SIMULATE - // Connect to simulator - pcieLink = connectToPCIeStream(PCIESTREAM_SIM); - #else - // Connect to pciestreamd - pcieLink = connectToPCIeStream(PCIESTREAM); - #endif - - // Power up mesh boards - #ifndef SIMULATE - sleep(1); - powerup(); - sleep(1); - waitForFPGAs(numBoards); - #endif - - // Create a DebugLink (UART) for each board - debugLinks = new DebugLink [numBoards]; - - // Initialise line buffers - for (int x = 0; x < TinselMeshXLen; x++) - for (int y = 0; y < TinselMeshYLen; y++) - for (int c = 0; c < TinselCoresPerBoard; c++) - for (int t = 0; t < TinselThreadsPerCore; t++) - lineBufferLen[x][y][c][t] = 0; - - // Open each UART - #ifdef SIMULATE - // Worker boards - int count = 0; - for (int y = 0; y < TinselMeshYLen; y++) - for (int x = 0; x < TinselMeshXLen; x++) { - int boardId = (y<> TinselMeshXBits; - if (x >= TinselMeshXLen) { - fprintf(stderr, "Mesh X dimension out of range: %d\n", x); - exit(EXIT_FAILURE); - } - if (y >= TinselMeshYLen) { - fprintf(stderr, "Mesh Y dimension out of range: %d\n", y); - exit(EXIT_FAILURE); - } - if (mesh[x][y] != NULL) { - fprintf(stderr, "Non-unique board id: %d\n", boardId); - exit(EXIT_FAILURE); - } - mesh[x][y] = &debugLinks[i]; - } - } -} - -// Destructor -HostLink::~HostLink() -{ - // Close debug link to the bridge board - bridgeBoard->close(); - // Close debug links to the mesh boards - for (int x = 0; x < TinselMeshXLen; x++) - for (int y = 0; y < TinselMeshYLen; y++) - mesh[x][y]->close(); - delete [] debugLinks; - // Close connections to the PCIe stream daemon - close(pcieLink); - // Release HostLink lock - if (flock(lockFile, LOCK_UN) != 0) { - perror("Failed to release HostLink lock"); - } - close(lockFile); -} - -// Power up the mesh boards -void powerup() -{ - #ifndef SIMULATE - // Disable power to the mesh boards - powerEnable(1); - #endif -} - -// Powerdown the mesh boards -void powerdown() -{ - #ifndef SIMULATE - // Disable power to the mesh boards - powerEnable(0); - #endif -} - -// Address construction -uint32_t HostLink::toAddr(uint32_t meshX, uint32_t meshY, - uint32_t coreId, uint32_t threadId) -{ - uint32_t addr; - addr = meshY; - addr = (addr << TinselMeshXBits) | meshX; - addr = (addr << TinselLogCoresPerBoard) | coreId; - addr = (addr << TinselLogThreadsPerCore) | threadId; - return addr; -} - -// Address deconstruction -void HostLink::fromAddr(uint32_t addr, uint32_t* meshX, uint32_t* meshY, - uint32_t* coreId, uint32_t* threadId) -{ - *threadId = addr % (1 << TinselLogThreadsPerCore); - addr >>= TinselLogThreadsPerCore; - - *coreId = addr % (1 << TinselLogCoresPerBoard); - addr >>= TinselLogCoresPerBoard; - - *meshX = addr % (1 << TinselMeshXBits); - addr >>= TinselMeshXBits; - - *meshY = addr; -} - -// Inject a message via PCIe (blocking) -void HostLink::send(uint32_t dest, uint32_t numFlits, void* payload) -{ - // Ensure that MaxFlitsPerMsg is not violated - assert(numFlits > 0 && numFlits <= TinselMaxFlitsPerMsg); - - // We assume that message flits are 128 bits - // (Because PCIeStream currently has this assumption) - assert(TinselLogBytesPerFlit == 4); - - // Message buffer - uint32_t buffer[4*(TinselMaxFlitsPerMsg+1)]; - - // Fill in the message header - // (See DE5BridgeTop.bsv for details) - buffer[0] = dest; - buffer[1] = 0; - buffer[2] = (numFlits-1) << 24; - buffer[3] = 0; - - // Bytes in payload - int payloadBytes = numFlits*16; - - // Fill in message payload - memcpy(&buffer[4], payload, payloadBytes); - - // Total bytes to send, including header - int totalBytes = 16+payloadBytes; - - // We assume that totalBytes is less than PIPE_BUF bytes, - // and write() will not send fewer PIPE_BUF bytes. - int ret = write(pcieLink, buffer, totalBytes); - if (ret != totalBytes) { - fprintf(stderr, "Error writing to PCIeStream: totalBytes = %d, " - "PIPE_BUF=%d, ret=%d.\n", totalBytes, PIPE_BUF, ret); - exit(EXIT_FAILURE); - } -} - -// Can send a message without blocking? -bool HostLink::canSend() -{ - pollfd pfd; - pfd.fd = pcieLink; - pfd.events = POLLOUT; - return poll(&pfd, 1, 0) > 0; -} - -// Receive a flit via PCIe (blocking) -void HostLink::recv(void* flit) -{ - int numBytes = 1 << TinselLogBytesPerFlit; - uint8_t* ptr = (uint8_t*) flit; - while (numBytes > 0) { - int n = read(pcieLink, (char*) ptr, numBytes); - if (n <= 0) { - fprintf(stderr, "Error reading from PCIeStream\n"); - exit(EXIT_FAILURE); - } - ptr += n; - numBytes -= n; - } -} - -// Receive a message (blocking), given size of message in bytes -void HostLink::recvMsg(void* msg, uint32_t numBytes) -{ - // Number of flits needed to hold message of size numBytes - int numFlits = 1 + ((numBytes-1) >> TinselLogBytesPerFlit); - - // Number of padding bytes that need to be received but not stored - int paddingBytes = (numFlits << TinselLogBytesPerFlit) - numBytes; - - // Fill message - uint8_t* ptr = (uint8_t*) msg; - while (numBytes > 0) { - int n = read(pcieLink, (char*) ptr, numBytes); - if (n <= 0) { - fprintf(stderr, "Error reading from PCIeStream\n"); - exit(EXIT_FAILURE); - } - ptr += n; - numBytes -= n; - } - - // Discard padding bytes - while (paddingBytes > 0) { - uint8_t padding[1 << TinselLogBytesPerFlit]; - int n = read(pcieLink, (char*) padding, paddingBytes); - if (n <= 0) { - fprintf(stderr, "Error reading from PCIeStream\n"); - exit(EXIT_FAILURE); - } - paddingBytes -= n; - } -} - -// Can receive a flit without blocking? -bool HostLink::canRecv() -{ - pollfd pfd; - pfd.fd = pcieLink; - pfd.events = POLLIN; - return poll(&pfd, 1, 0) > 0; -} - -// Load application code and data onto the mesh -void HostLink::boot(const char* codeFilename, const char* dataFilename) -{ - MemFileReader code(codeFilename); - MemFileReader data(dataFilename); - - // Request to boot loader - BootReq req; - - // Total number of cores - const uint32_t numCores = - (TinselMeshXLen*TinselMeshYLen) << TinselLogCoresPerBoard; - - // Step 1: load code into instruction memory - // ----------------------------------------- - - uint32_t addrReg = 0xffffffff; - uint32_t addr, word; - while (code.getWord(&addr, &word)) { - // Send instruction to each core - for (int x = 0; x < TinselMeshXLen; x++) { - for (int y = 0; y < TinselMeshYLen; y++) { - for (int i = 0; i < (1 << TinselLogCoresPerBoard); i++) { - uint32_t dest = toAddr(x, y, i, 0); - if (addr != addrReg) { - req.cmd = SetAddrCmd; - req.numArgs = 1; - req.args[0] = addr; - send(dest, 1, &req); - } - req.cmd = WriteInstrCmd; - req.numArgs = 1; - req.args[0] = word; - send(dest, 1, &req); - } - } - } - addrReg = addr + 4; - } - - // Step 2: initialise data memory - // ------------------------------ - - // Compute number of cores per DRAM - const uint32_t coresPerDRAM = 1 << - (TinselLogCoresPerDCache + TinselLogDCachesPerDRAM); - - // Write data to DRAMs - addrReg = 0xffffffff; - while (data.getWord(&addr, &word)) { - for (int x = 0; x < TinselMeshXLen; x++) { - for (int y = 0; y < TinselMeshYLen; y++) { - for (int i = 0; i < TinselDRAMsPerBoard; i++) { - // Use one core to initialise each DRAM - uint32_t dest = toAddr(x, y, coresPerDRAM * i, 0); - if (addr != addrReg) { - req.cmd = SetAddrCmd; - req.numArgs = 1; - req.args[0] = addr; - send(dest, 1, &req); - } - req.cmd = StoreCmd; - req.numArgs = 1; - req.args[0] = word; - send(dest, 1, &req); - } - } - } - addrReg = addr + 4; - } - - // Step 3: start cores - // ------------------- - - // Send start command - uint32_t started = 0; - uint8_t flit[4 << TinselLogWordsPerFlit]; - for (int x = 0; x < TinselMeshXLen; x++) { - for (int y = 0; y < TinselMeshYLen; y++) { - for (int i = 0; i < (1 << TinselLogCoresPerBoard); i++) { - while (!canSend()) { - if (canRecv()) { - recv(flit); - started++; - } - } - uint32_t dest = toAddr(x, y, i, 0); - req.cmd = StartCmd; - req.args[0] = (1<setBroadcastDest(0); - mesh[x][y]->put(0); - } - } -} - -// Load instructions into given core's instruction memory -void HostLink::loadInstrsOntoCore(const char* codeFilename, - uint32_t meshX, uint32_t meshY, uint32_t coreId) -{ - // Code file - MemFileReader code(codeFilename); - - // Load loop - BootReq req; - uint32_t addrReg = 0xffffffff; - uint32_t addr, word; - uint32_t dest = toAddr(meshX, meshY, coreId, 0); - while (code.getWord(&addr, &word)) { - // Write instruction - if (addr != addrReg) { - req.cmd = SetAddrCmd; - req.numArgs = 1; - req.args[0] = addr; - send(dest, 1, &req); - } - req.cmd = WriteInstrCmd; - req.numArgs = 1; - req.args[0] = word; - send(dest, 1, &req); - addrReg = addr + 4; - } -} - -// Load data via given core on given board -void HostLink::loadDataViaCore(const char* dataFilename, - uint32_t meshX, uint32_t meshY, uint32_t coreId) -{ - MemFileReader data(dataFilename); - - // Write data to DRAM - BootReq req; - uint32_t addrReg = 0xffffffff; - uint32_t addr, word; - uint32_t dest = toAddr(meshX, meshY, coreId, 0); - while (data.getWord(&addr, &word)) { - // Write data - if (addr != addrReg) { - req.cmd = SetAddrCmd; - req.numArgs = 1; - req.args[0] = addr; - send(dest, 1, &req); - } - req.cmd = StoreCmd; - req.numArgs = 1; - req.args[0] = word; - send(dest, 1, &req); - addrReg = addr + 4; - } -} - -// Start given number of threads on given core -void HostLink::startOne(uint32_t meshX, uint32_t meshY, - uint32_t coreId, uint32_t numThreads) -{ - assert(numThreads > 0 && numThreads <= TinselThreadsPerCore); - - BootReq req; - uint32_t dest = toAddr(meshX, meshY, coreId, 0); - - // Send start command - req.cmd = StartCmd; - req.args[0] = numThreads-1; - send(dest, 1, &req); - - // Wait for start response - uint8_t flit[4 << TinselLogWordsPerFlit]; - recv(flit); -} - -// Trigger application execution on all started threads on given core -void HostLink::goOne(uint32_t meshX, uint32_t meshY, uint32_t coreId) -{ - mesh[meshX][meshY]->setDest(coreId, 0); - mesh[meshX][meshY]->put(0); -} - -// Set address for remote memory access on given board via given core -// (This address is auto-incremented on loads and stores) -void HostLink::setAddr(uint32_t meshX, uint32_t meshY, - uint32_t coreId, uint32_t addr) -{ - BootReq req; - req.cmd = SetAddrCmd; - req.numArgs = 1; - req.args[0] = addr; - send(toAddr(meshX, meshY, coreId, 0), 1, &req); -} - -// Store words to remote memory on a given board via given core -void HostLink::store(uint32_t meshX, uint32_t meshY, - uint32_t coreId, uint32_t numWords, uint32_t* data) -{ - BootReq req; - req.cmd = StoreCmd; - while (numWords > 0) { - uint32_t sendWords = numWords > 15 ? 15 : numWords; - numWords = numWords - sendWords; - req.numArgs = sendWords; - for (int i = 0; i < sendWords; i++) req.args[i] = data[i]; - uint32_t numFlits = 1 + (sendWords >> 2); - send(toAddr(meshX, meshY, coreId, 0), numFlits, &req); - } -} - -// Redirect UART StdOut to given file -// Returns false when no data has been emitted -bool HostLink::pollStdOut(FILE* outFile) -{ - bool got = false; - for (int x = 0; x < TinselMeshXLen; x++) { - for (int y = 0; y < TinselMeshYLen; y++) { - if (mesh[x][y]->canGet()) { - // Receive byte - uint8_t byte; - uint32_t c, t; - mesh[x][y]->get(&c, &t, &byte); - got = true; - - // Update line buffer & display on newline or buffer-full - int len = lineBufferLen[x][y][c][t]; - if (byte == '\n' || len == MaxLineLen-1) { - lineBuffer[x][y][c][t][len] = '\0'; - fprintf(outFile, "%d:%d:%d:%d: %s\n", x, y, c, t, - lineBuffer[x][y][c][t]); - lineBufferLen[x][y][c][t] = len = 0; - } - if (byte != '\n') { - lineBuffer[x][y][c][t][len] = byte; - lineBufferLen[x][y][c][t]++; - } - } - } - } - - return got; -} - -// Redirect UART StdOut to stdout -// Returns false when no data has been emitted -bool HostLink::pollStdOut() -{ - pollStdOut(stdout); -} - -// Redirect UART StdOut to given file (blocking function, never terminates) -void HostLink::dumpStdOut(FILE* outFile) -{ - for (;;) { - bool ok = pollStdOut(outFile); - if (!ok) usleep(10000); - } -} - -// Display UART StdOut (blocking function, never terminates) -void HostLink::dumpStdOut() -{ - dumpStdOut(stdout); -} diff --git a/Source/HostLink/HostLink.h b/Source/HostLink/HostLink.h deleted file mode 100755 index 42d26fa3..00000000 --- a/Source/HostLink/HostLink.h +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef _HOSTLINK_H_ -#define _HOSTLINK_H_ - -#include -#include -#include -#include -#include "DebugLink.h" - -// Max line length for line-buffered UART StdOut capture -#define MaxLineLen 128 - -// Connections to PCIeStream -#define PCIESTREAM "pciestream" -#define PCIESTREAM_SIM "tinsel.b-1.5" - -// Power down the mesh boards -void powerdown(); - -// Power up the mesh boards -void powerup(); - -class HostLink { - // JTAG UART connections - DebugLink* debugLinks; - - // Lock file for acquring exclusive access to PCIeStream - int lockFile; - - // File descriptor for link to PCIeStream - int pcieLink; - - // Line buffers for JTAG UART StdOut - char lineBuffer[TinselMeshXLen][TinselMeshYLen] - [TinselCoresPerBoard][TinselThreadsPerCore] - [MaxLineLen]; - int lineBufferLen[TinselMeshXLen][TinselMeshYLen] - [TinselCoresPerBoard][TinselThreadsPerCore]; - public: - - // Constructor - HostLink(); - - // Destructor - ~HostLink(); - - // Debug links - // ----------- - - // Link to the bridge board (opened by constructor) - DebugLink* bridgeBoard; - - // Links to the mesh boards (opened by constructor) - DebugLink* mesh[TinselMeshXLen][TinselMeshYLen]; - - // Send and receive messages over PCIe - // ----------------------------------- - - // Send a message (blocking) - void send(uint32_t dest, uint32_t numFlits, void* msg); - - // Can send a message without blocking? - bool canSend(); - - // Receive a flit (blocking) - void recv(void* flit); - - // Can receive a flit without blocking? - bool canRecv(); - - // Receive a message (blocking), given size of message in bytes - void recvMsg(void* msg, uint32_t numBytes); - - // Address construction/deconstruction - // ----------------------------------- - - // Address construction - uint32_t toAddr(uint32_t meshX, uint32_t meshY, - uint32_t coreId, uint32_t threadId); - - // Address deconstruction - void fromAddr(uint32_t addr, uint32_t* meshX, uint32_t* meshY, - uint32_t* coreId, uint32_t* threadId); - - // Assuming the boot loader is running on the cores - // ------------------------------------------------ - // - // (Only thread 0 on each core is active when the boot loader is running) - - // Load application code and data onto the mesh - void boot(const char* codeFilename, const char* dataFilename); - - // Trigger to start application execution - void go(); - - // Set address for remote memory access to given board via given core - // (This address is auto-incremented on loads and stores) - void setAddr(uint32_t meshX, uint32_t meshY, - uint32_t coreId, uint32_t addr); - - // Store words to remote memory on given board via given core - void store(uint32_t meshX, uint32_t meshY, - uint32_t coreId, uint32_t numWords, uint32_t* data); - - // Finer-grained control over application loading and execution - // ------------------------------------------------------------ - - // Load instructions into given core's instruction memory - void loadInstrsOntoCore(const char* codeFilename, - uint32_t meshX, uint32_t meshY, uint32_t coreId); - - // Load data via given core on given board - void loadDataViaCore(const char* dataFilename, - uint32_t meshX, uint32_t meshY, uint32_t coreId); - - // Start given number of threads on given core - void startOne(uint32_t meshX, uint32_t meshY, - uint32_t coreId, uint32_t numThreads); - - // Trigger application execution on all started threads on given core - void goOne(uint32_t meshX, uint32_t meshY, uint32_t coreId); - - // Line-buffered StdOut console - // ---------------------------- - - // Receive StdOut byte streams and append to file (non-blocking) - bool pollStdOut(FILE* outFile); - - // Receive StdOut byte streams and display on stdout (non-blocking) - bool pollStdOut(); - - // Receive StdOut byte streams and append to file (non-terminating) - void dumpStdOut(FILE* outFile); - - // Receive StdOut byte streams and display on stdout (non-terminating) - void dumpStdOut(); -}; - -#endif diff --git a/Source/HostLink/JtagAtlantic.h b/Source/HostLink/JtagAtlantic.h deleted file mode 100755 index b3c864f9..00000000 --- a/Source/HostLink/JtagAtlantic.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2015 Theo Markettos - * Copyright (c) 2016 Matthew Naylor - * All rights reserved. - * - * This software was developed by SRI International and the University of - * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 - * ("CTSRD"), as part of the DARPA CRASH research programme. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _JTAGATLANTIC_H_ -#define _JTAGATLANTIC_H_ - -// Header for the libjtag_atlantic shared library provided by Altera - -typedef struct JTAGATLANTIC JTAGATLANTIC; - -extern JTAGATLANTIC *jtagatlantic_open( - const char *chain, - int device_index, - int link_instance, - const char *app_name); - -extern int jtagatlantic_get_error(const char **other_info); - -extern void jtagatlantic_close(JTAGATLANTIC *link); - -extern int jtagatlantic_write( - JTAGATLANTIC *link, - const char *data, - unsigned int count); - -extern int jtagatlantic_flush(JTAGATLANTIC *link); - -extern int jtagatlantic_read( - JTAGATLANTIC *link, - char *buffer, - unsigned int buffsize); - -extern int jtagatlantic_is_setup_done(JTAGATLANTIC *link); - -extern int jtagatlantic_wait_open(JTAGATLANTIC *link); - -extern int jtagatlantic_bytes_available(JTAGATLANTIC *link); - -extern void jtagatlantic_get_info( - JTAGATLANTIC *link, - char const **cable, - int *device, - int *instance); - -extern int jtagatlantic_cable_warning(JTAGATLANTIC *link); - -#endif diff --git a/Source/HostLink/Makefile b/Source/HostLink/Makefile deleted file mode 100755 index 9119d693..00000000 --- a/Source/HostLink/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -TINSEL_ROOT = .. -include $(TINSEL_ROOT)/globals.mk - -ifndef QUARTUS_ROOTDIR - $(error Please set QUARTUS_ROOTDIR) -endif - -# Local compiler flags -CPPFLAGS = -I$(INC) -O2 - -# Top-level dependencies -DEPS = $(INC)/config.h $(INC)/boot.h \ - DebugLink.h HostLink.h JtagAtlantic.h MemFileReader.h UART.h - -.PHONY: all -all: DebugLink.o HostLink.o MemFileReader.o UART.o pciestreamd \ - sim/DebugLink.o sim/HostLink.o sim/MemFileReader.o sim/UART.o \ - udsock tinsel-power - -pciestreamd: pciestreamd.cpp PowerLink.o - g++ -O2 pciestreamd.cpp PowerLink.o -o pciestreamd - -udsock: udsock.c - gcc -O2 udsock.c -o udsock - -tinsel-power: tinsel-power.cpp PowerLink.o - g++ $(CPPFLAGS) tinsel-power.cpp PowerLink.o -o tinsel-power - -sim/%.o: %.cpp $(DEPS) - mkdir -p sim - g++ -DSIMULATE -o $@ $(CPPFLAGS) -c $< - -%.o: %.cpp $(DEPS) - g++ -o $@ $(CPPFLAGS) -c $< - -$(INC)/config.h: $(TINSEL_ROOT)/config.py - make -C $(INC) - -.PHONY: clean -clean: - rm -f *.o pciestreamd udsock tinsel-power - rm -rf sim diff --git a/Source/HostLink/MemFileReader.cpp b/Source/HostLink/MemFileReader.cpp deleted file mode 100755 index 20423de7..00000000 --- a/Source/HostLink/MemFileReader.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Memory files in "verilog" format, produced by objcopy, have the -// following structure: -// -// @00000200 -// 6F 00 40 00 13 01 -// -// @00100000 -// 48 65 6C 6C 6F 20 66 72 6F 6D 20 74 68 72 65 61 -// 64 20 30 78 25 78 0A 00 -// -// The @ sign denotes a start address. The hex bytes that follow it, -// up to the next @ sign, are a contiguous stream of bytes starting -// at that address. - -#include "MemFileReader.h" -#include -#include -#include -#include - -// Constructor -MemFileReader::MemFileReader(const char* filename) -{ - address = 0; - fp = fopen(filename, "rt"); - if (fp == NULL) { - fprintf(stderr, "Failed to open file '%s'\n", filename); - exit(EXIT_FAILURE); - } -} - -// Read a byte -bool MemFileReader::getByte(uint32_t* addr, uint8_t* byte) -{ - assert(fp != NULL); - uint32_t data; - *addr = address; - while (fscanf(fp, " @%x", &address) > 0) *addr = address; - if (fscanf(fp, " %x", &data) > 0) { - *byte = (uint8_t) data; - address++; - return true; - } - return false; -} - -// Read a 32-bit word -bool MemFileReader::getWord(uint32_t* addr, uint32_t* word) -{ - assert(fp != NULL); - *word = 0; - uint8_t* bytePtr = (uint8_t*) word; - uint32_t data; - *addr = address; - int count = 0; - while (fscanf(fp, " @%x", &address) > 0) *addr = address; - while (fscanf(fp, " %x", &data) > 0) { - bytePtr[count] = (uint8_t) data; - count++; - if (count == 4) break; - } - if (count != 0) { - address += 4; - return true; - } - return false; -} - -// Destructor -MemFileReader::~MemFileReader() -{ - if (fp != NULL) fclose(fp); -} diff --git a/Source/HostLink/MemFileReader.h b/Source/HostLink/MemFileReader.h deleted file mode 100755 index 7253e85d..00000000 --- a/Source/HostLink/MemFileReader.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef _MEMFILEREADER_H_ -#define _MEMFILEREADER_H_ - -#include -#include -#include - -class MemFileReader { - FILE* fp; - uint32_t address; - - public: - // Constructor - MemFileReader(const char* filename); - - // Read a byte - bool getByte(uint32_t* addr, uint8_t* byte); - - // Read a 32-bit word - bool getWord(uint32_t* addr, uint32_t* word); - - // Destructor - ~MemFileReader(); -}; - -#endif diff --git a/Source/HostLink/Monitor/Monitor.cpp b/Source/HostLink/Monitor/Monitor.cpp deleted file mode 100755 index 76c1b156..00000000 --- a/Source/HostLink/Monitor/Monitor.cpp +++ /dev/null @@ -1,70 +0,0 @@ -//------------------------------------------------------------------------------ - -#include "Monitor.h" -#include "CommonBase.h" -#include "PMsg_p.hpp" -#include "mpi.h" -#include "Pglobals.h" -#include "jnj.h" -#include - -//============================================================================== - -Monitor::Monitor(int argc,char * argv[],string d): - CommonBase(argc,argv,d,string(__FILE__)) -{ - // File to hold stuff sent to monitor -sMonFile = pPmap->M[Urank] + "_" + uint2str(Urank); -for(unsigned i=0;iDump(fMonFile); -fprintf(fMonFile,"............................................\n"); - -return 0; -} - -//============================================================================== - diff --git a/Source/HostLink/Monitor/Monitor.h b/Source/HostLink/Monitor/Monitor.h deleted file mode 100755 index 510f66ca..00000000 --- a/Source/HostLink/Monitor/Monitor.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef __MonitorH__H -#define __MonitorH__H - -#include "CommonBase.h" -#include "Environment.h" -#include "Pglobals.h" - -//============================================================================== - -class Monitor : public CommonBase -{ - -public: - Monitor(int,char **,string); -virtual ~ Monitor(); - -private: -#include "Decode.cpp" -void Dump(FILE * = stdout); -unsigned OnNull(PMsg_p *); - -typedef unsigned (Monitor::*pMeth)(PMsg_p *); -map FnMapx; - -FILE * fMonFile; -string sMonFile; -unsigned item; -}; - -//============================================================================== - -#endif - diff --git a/Source/HostLink/Monitor/Monitor.~cpp b/Source/HostLink/Monitor/Monitor.~cpp deleted file mode 100755 index e2faee15..00000000 --- a/Source/HostLink/Monitor/Monitor.~cpp +++ /dev/null @@ -1,70 +0,0 @@ -//------------------------------------------------------------------------------ - -#include "Monitor.h" -#include "CommonBase.h" -#include "PMsg_p.hpp" -#include "mpi.h" -#include "Pglobals.h" -#include "jnj.h" -#include - -//============================================================================== - -Monitor::Monitor(int argc,char * argv[],string d): - CommonBase(argc,argv,d,string(__FILE__)) -{ - // File to hold stuff sent to monitor -sMonFile = pPmap->M[Urank] + uint2str(Urank); -for(unsigned i=0;iDump(fMonFile); -fprintf(fMonFile,"............................................\n"); - -return 0; -} - -//============================================================================== - diff --git a/Source/HostLink/Monitor/MonitorMain.cpp b/Source/HostLink/Monitor/MonitorMain.cpp deleted file mode 100755 index 6a852016..00000000 --- a/Source/HostLink/Monitor/MonitorMain.cpp +++ /dev/null @@ -1,34 +0,0 @@ -//------------------------------------------------------------------------------ - -#include "Monitor.h" -#include "Unrec_t.h" -#include "Pglobals.h" -#include - -//------------------------------------------------------------------------------ - -int main(int argc, char* argv[]) -{ -Monitor * pMonitor = 0; -try { - pMonitor = new Monitor(argc,argv,string(csMONITORproc)); -} -catch(bad_alloc) { - printf("\n\n%s Main out of memory... \n\n",csMONITORproc); - fflush(stdout); -} -catch(Unrec_t u) { - u.Post(); -} -catch(...) { - printf("\n\n%s Main unhandled exception...??? \n\n",csMONITORproc); - fflush(stdout); -} -printf("%s Main closing down\n",csMONITORproc); -fflush(stdout); -delete pMonitor; -return 0; -} - -//------------------------------------------------------------------------------ - \ No newline at end of file diff --git a/Source/HostLink/Monitor/PowerLink.cpp b/Source/HostLink/Monitor/PowerLink.cpp deleted file mode 100755 index 207f7200..00000000 --- a/Source/HostLink/Monitor/PowerLink.cpp +++ /dev/null @@ -1,246 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "PowerLink.h" - -#define DEFAULT_TINSEL_ROOT "/local/tinsel" - -// Open a power link -int powerInit(char* dev) -{ - int fd = open(dev, O_RDWR | O_NONBLOCK | O_NOCTTY); - if (fd == -1) { - perror("open"); - exit(EXIT_FAILURE); - } - - // Get attributes - struct termios tty; - if (tcgetattr(fd, &tty) == -1) { - fprintf(stderr, "Can't get termios attributes on '%s'\n", dev); - exit(EXIT_FAILURE); - } - - // Baud rate - cfsetospeed(&tty, B115200); - cfsetispeed(&tty, B115200); - // Raw mode - cfmakeraw(&tty); - tty.c_cc[VMIN] = 1; - tty.c_cc[VTIME] = 0; - // No parity - tty.c_cflag &= ~(PARENB | PARODD | CMSPAR); - // 8 data bits - tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; - // 1 stop bit - tty.c_cflag &= ~CSTOPB; - // No flow control - tty.c_cflag &= ~(CRTSCTS); - tty.c_iflag &= ~(IXON | IXOFF | IXANY); - // Local - tty.c_cflag |= CLOCAL; - // HUP on close - //tty.c_cflag |= HUPCL; - tty.c_cflag &= ~HUPCL; - - // Set attributes - if (tcsetattr(fd, TCSANOW, &tty) == -1) { - fprintf(stderr, "Can't set termios attributes on '%s'\n", dev); - exit(EXIT_FAILURE); - } - - return fd; -} - -// Reset the PowerLink PSoCs -// (Starting from link with given index) -void powerResetPSoCs(int index) -{ - char* root = getenv("TINSEL_ROOT"); - if (root == NULL) { - root = (char*) DEFAULT_TINSEL_ROOT; - } - char cmd[1024]; - snprintf(cmd, sizeof(cmd), "%s/bin/reset-psocs.sh %d", root, index); - if (system(cmd) < 0) { - fprintf(stderr, "Can't run '%s'\n", cmd); - exit(EXIT_FAILURE); - } -} - -// Send a command over a power link -int powerPutCmd(int fd, char* cmd, char* resp, int respSize) -{ - // For 'select' call - struct timeval tv; - fd_set fds; - - // Send command - char* ptr = cmd; - while (*ptr) { - // Initialise descriptor set - FD_ZERO(&fds); - FD_SET(fd, &fds); - // Set timeout - tv.tv_sec = 1; - tv.tv_usec = 0; - // Wait until write is possible - int ret = select(fd+1, NULL, &fds, NULL, &tv); - if (ret < 0) { - perror("select() on power link"); - return -1; - } - else if (ret == 0) { - // Timeout elapsed - //fprintf(stderr, "timeout on power link write\n"); - return -1; - } - else { - // Do the write - int n = write(fd, ptr, 1); - if (n == -1) { - perror("write() on power link"); - return -1; - } - if (n == 1) ptr++; - } - } - // Receive response - int got = 0; - while (got < (respSize-1)) { - // Initialise descriptor set - FD_ZERO(&fds); - FD_SET(fd, &fds); - // Set timeout - tv.tv_sec = 1; - tv.tv_usec = 0; - // Wait until read is possible - int ret = select(fd+1, &fds, NULL, NULL, &tv); - if (ret < 0) { - perror("select() on power link"); - return -1; - } - else if (ret == 0) { - // Timeout elapsed - //fprintf(stderr, "timeout on power link read"); - return -1; - } - else { - char c; - int n = read(fd, &c, 1); - if (n == -1) { - perror("read() on power link"); - return -1; - } - if (n == 1) { - if (c == '!') { - resp[got] = '\0'; - return 0; - } - resp[got++] = c; - } - } - } - resp[got] = '\0'; - return 0; -} - -// Send a command over all power links -int powerPutCmdAll(const char* cmd, bool retry) -{ - int retryCount = 0; - // Retry from here, if necessary - retry_label: - // Determine all the power links - char line[256]; - FILE* fp = popen( - "ls /dev/serial/by-id/usb-Cypress_Semiconductor_USBUART*", "r"); - if (!fp) { - fprintf(stderr, "Power-enable failed\n"); - return -1; - } - // For each power link - for (int i = 0; ; i++) { - if (fgets(line, sizeof(line), fp) == NULL) break; - if (feof(fp)) break; - // Trim the new line - char* ptr = line; - while (*ptr) { if (*ptr == '\n') *ptr = '\0'; ptr++; } - // Open link - int fd = powerInit(line); - // Send command - char resp[256]; - int ok = powerPutCmd(fd, (char*) cmd, resp, sizeof(resp)); - // On error, optionally reset PSoCs and retry - if (ok < 0) { - if (retry && retryCount < 2) { - retryCount++; - powerResetPSoCs(0); - close(fd); - fclose(fp); - goto retry_label; - } - else { - fprintf(stderr, "Temporarily unable to connect to PowerLinks\n"); - return -1; - } - } - // Close link - close(fd); - } - fclose(fp); - return 0; -} - -// Enable power to all worker FPGAs -int powerEnable(int enable) -{ - // Try to talk to power boards and reset them if neccessary - // (This is a workaround for occasionally unresponsive powers boards) - int ret = powerPutCmdAll("p?", true); - if (ret < 0) return -1; - // Now enable or disable the power - if (enable) - return powerPutCmdAll("p=1.", false); - else - return powerPutCmdAll("p=0.", false); -} - -// Disable then enable power to all worker FPGAs -int powerReset() -{ - int ret = powerEnable(0); - if (ret < 0) { - fprintf(stderr, "Failed to reset FPGAs.\n"); - return -1; - } - sleep(3); - ret = powerEnable(1); - if (ret < 0) { - fprintf(stderr, "Failed to reset FPGAs.\n"); - return -1; - } - return 0; -} - -// Wait for FPGAs to be detected after powerup -void waitForFPGAs(int numFPGAs) -{ - char* root = getenv("TINSEL_ROOT"); - if (root == NULL) { - root = (char*) DEFAULT_TINSEL_ROOT; - } - char cmd[1024]; - snprintf(cmd, sizeof(cmd), "%s/bin/wait-for-fpgas.sh %d", root, numFPGAs); - if (system(cmd) < 0) { - fprintf(stderr, "Can't run '%s'\n", cmd); - exit(EXIT_FAILURE); - } -} diff --git a/Source/HostLink/PowerLink.cpp b/Source/HostLink/PowerLink.cpp deleted file mode 100755 index 207f7200..00000000 --- a/Source/HostLink/PowerLink.cpp +++ /dev/null @@ -1,246 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "PowerLink.h" - -#define DEFAULT_TINSEL_ROOT "/local/tinsel" - -// Open a power link -int powerInit(char* dev) -{ - int fd = open(dev, O_RDWR | O_NONBLOCK | O_NOCTTY); - if (fd == -1) { - perror("open"); - exit(EXIT_FAILURE); - } - - // Get attributes - struct termios tty; - if (tcgetattr(fd, &tty) == -1) { - fprintf(stderr, "Can't get termios attributes on '%s'\n", dev); - exit(EXIT_FAILURE); - } - - // Baud rate - cfsetospeed(&tty, B115200); - cfsetispeed(&tty, B115200); - // Raw mode - cfmakeraw(&tty); - tty.c_cc[VMIN] = 1; - tty.c_cc[VTIME] = 0; - // No parity - tty.c_cflag &= ~(PARENB | PARODD | CMSPAR); - // 8 data bits - tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; - // 1 stop bit - tty.c_cflag &= ~CSTOPB; - // No flow control - tty.c_cflag &= ~(CRTSCTS); - tty.c_iflag &= ~(IXON | IXOFF | IXANY); - // Local - tty.c_cflag |= CLOCAL; - // HUP on close - //tty.c_cflag |= HUPCL; - tty.c_cflag &= ~HUPCL; - - // Set attributes - if (tcsetattr(fd, TCSANOW, &tty) == -1) { - fprintf(stderr, "Can't set termios attributes on '%s'\n", dev); - exit(EXIT_FAILURE); - } - - return fd; -} - -// Reset the PowerLink PSoCs -// (Starting from link with given index) -void powerResetPSoCs(int index) -{ - char* root = getenv("TINSEL_ROOT"); - if (root == NULL) { - root = (char*) DEFAULT_TINSEL_ROOT; - } - char cmd[1024]; - snprintf(cmd, sizeof(cmd), "%s/bin/reset-psocs.sh %d", root, index); - if (system(cmd) < 0) { - fprintf(stderr, "Can't run '%s'\n", cmd); - exit(EXIT_FAILURE); - } -} - -// Send a command over a power link -int powerPutCmd(int fd, char* cmd, char* resp, int respSize) -{ - // For 'select' call - struct timeval tv; - fd_set fds; - - // Send command - char* ptr = cmd; - while (*ptr) { - // Initialise descriptor set - FD_ZERO(&fds); - FD_SET(fd, &fds); - // Set timeout - tv.tv_sec = 1; - tv.tv_usec = 0; - // Wait until write is possible - int ret = select(fd+1, NULL, &fds, NULL, &tv); - if (ret < 0) { - perror("select() on power link"); - return -1; - } - else if (ret == 0) { - // Timeout elapsed - //fprintf(stderr, "timeout on power link write\n"); - return -1; - } - else { - // Do the write - int n = write(fd, ptr, 1); - if (n == -1) { - perror("write() on power link"); - return -1; - } - if (n == 1) ptr++; - } - } - // Receive response - int got = 0; - while (got < (respSize-1)) { - // Initialise descriptor set - FD_ZERO(&fds); - FD_SET(fd, &fds); - // Set timeout - tv.tv_sec = 1; - tv.tv_usec = 0; - // Wait until read is possible - int ret = select(fd+1, &fds, NULL, NULL, &tv); - if (ret < 0) { - perror("select() on power link"); - return -1; - } - else if (ret == 0) { - // Timeout elapsed - //fprintf(stderr, "timeout on power link read"); - return -1; - } - else { - char c; - int n = read(fd, &c, 1); - if (n == -1) { - perror("read() on power link"); - return -1; - } - if (n == 1) { - if (c == '!') { - resp[got] = '\0'; - return 0; - } - resp[got++] = c; - } - } - } - resp[got] = '\0'; - return 0; -} - -// Send a command over all power links -int powerPutCmdAll(const char* cmd, bool retry) -{ - int retryCount = 0; - // Retry from here, if necessary - retry_label: - // Determine all the power links - char line[256]; - FILE* fp = popen( - "ls /dev/serial/by-id/usb-Cypress_Semiconductor_USBUART*", "r"); - if (!fp) { - fprintf(stderr, "Power-enable failed\n"); - return -1; - } - // For each power link - for (int i = 0; ; i++) { - if (fgets(line, sizeof(line), fp) == NULL) break; - if (feof(fp)) break; - // Trim the new line - char* ptr = line; - while (*ptr) { if (*ptr == '\n') *ptr = '\0'; ptr++; } - // Open link - int fd = powerInit(line); - // Send command - char resp[256]; - int ok = powerPutCmd(fd, (char*) cmd, resp, sizeof(resp)); - // On error, optionally reset PSoCs and retry - if (ok < 0) { - if (retry && retryCount < 2) { - retryCount++; - powerResetPSoCs(0); - close(fd); - fclose(fp); - goto retry_label; - } - else { - fprintf(stderr, "Temporarily unable to connect to PowerLinks\n"); - return -1; - } - } - // Close link - close(fd); - } - fclose(fp); - return 0; -} - -// Enable power to all worker FPGAs -int powerEnable(int enable) -{ - // Try to talk to power boards and reset them if neccessary - // (This is a workaround for occasionally unresponsive powers boards) - int ret = powerPutCmdAll("p?", true); - if (ret < 0) return -1; - // Now enable or disable the power - if (enable) - return powerPutCmdAll("p=1.", false); - else - return powerPutCmdAll("p=0.", false); -} - -// Disable then enable power to all worker FPGAs -int powerReset() -{ - int ret = powerEnable(0); - if (ret < 0) { - fprintf(stderr, "Failed to reset FPGAs.\n"); - return -1; - } - sleep(3); - ret = powerEnable(1); - if (ret < 0) { - fprintf(stderr, "Failed to reset FPGAs.\n"); - return -1; - } - return 0; -} - -// Wait for FPGAs to be detected after powerup -void waitForFPGAs(int numFPGAs) -{ - char* root = getenv("TINSEL_ROOT"); - if (root == NULL) { - root = (char*) DEFAULT_TINSEL_ROOT; - } - char cmd[1024]; - snprintf(cmd, sizeof(cmd), "%s/bin/wait-for-fpgas.sh %d", root, numFPGAs); - if (system(cmd) < 0) { - fprintf(stderr, "Can't run '%s'\n", cmd); - exit(EXIT_FAILURE); - } -} diff --git a/Source/HostLink/PowerLink.h b/Source/HostLink/PowerLink.h deleted file mode 100755 index 6cf36748..00000000 --- a/Source/HostLink/PowerLink.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _POWER_LINK_H_ -#define _POWER_LINK_H_ - -// Functions to communicate with Cambridge Power Monitor -// (Only basic functionality for now) - -// Open a power link and return file descriptor, given device file -int powerInit(char* dev); - -// Send a command over a power link -int powerPutCmd(int fd, char* cmd, char* resp, int respSize); - -// Enable or disable power to all worker FPGAs -int powerEnable(int enable); - -// Disable then enable power to all worker FPGAs -int powerReset(); - -// Wait for FPGAs to be detected after powerup -void waitForFPGAs(int numFPGAs); - -#endif diff --git a/Source/HostLink/UART.cpp b/Source/HostLink/UART.cpp deleted file mode 100755 index e9a354d5..00000000 --- a/Source/HostLink/UART.cpp +++ /dev/null @@ -1,188 +0,0 @@ -#ifdef SIMULATE - -// ============================================================================= -// Simulation version -// ============================================================================= - -#include "UART.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Open UART socket -static int openSocket(int instId) -{ - // Create socket - int sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock == -1) { - perror("socket"); - return -1; - } - - // Connect to socket - struct sockaddr_un addr; - memset(&addr, 0, sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - snprintf(&addr.sun_path[1], sizeof(addr.sun_path)-2, "tinsel.b%i.0", instId); - addr.sun_path[0] = '\0'; - int ret = connect(sock, (struct sockaddr *) &addr, - sizeof(struct sockaddr_un)); - if (ret < 0) { - perror("connect"); - return -1; - } - - return sock; -} - -// Constructor -UART::UART() -{ - sock = -1; -} - -// Open UART with given instance id -void UART::open(int instId) -{ - instanceId = instId; -} - -// Send bytes over UART -void UART::put(void* buffer, int numBytes) -{ - if (sock == -1) sock = openSocket(instanceId); - uint8_t* ptr = (uint8_t*) buffer; - while (numBytes > 0) { - int n = write(sock, (void*) ptr, numBytes); - if (n <= 0) { - fprintf(stderr, "Error writing to UART %i\n", instanceId); - exit(EXIT_FAILURE); - } - ptr += n; - numBytes -= n; - } -} - -// Is a byte available for reading? -bool UART::canGet() -{ - if (sock == -1) sock = openSocket(instanceId); - pollfd pfd; - pfd.fd = sock; - pfd.events = POLLIN; - return poll(&pfd, 1, 0); -} - -// Receive bytes over UART -void UART::get(void* buffer, int numBytes) -{ - if (sock == -1) sock = openSocket(instanceId); - uint8_t* ptr = (uint8_t*) buffer; - while (numBytes > 0) { - int n = read(sock, (void*) ptr, numBytes); - if (n <= 0) { - fprintf(stderr, "Error reading UART %i\n", instanceId); - exit(EXIT_FAILURE); - } - ptr += n; - numBytes -= n; - } -} - -// Flush writes -void UART::flush() -{ - if (sock != -1) fsync(sock); -} - -// Close UART -void UART::close() -{ - if (sock != -1) ::close(sock); -} - -#else - -// ============================================================================= -// FPGA version -// ============================================================================= - -#include "UART.h" -#include "JtagAtlantic.h" - -// Constructor -UART::UART() -{ - jtag = NULL; - instanceId = -1; -} - -// Open UART with given instance id -void UART::open(int instId) -{ - char chain[256]; - snprintf(chain, sizeof(chain), "%i", instId); - jtag = jtagatlantic_open(chain, 0, 0, "hostlink"); - if (jtag == NULL) { - fprintf(stderr, "Error opening JTAG UART %i\n", instId); - exit(EXIT_FAILURE); - } - instanceId = instId; -} - -// Send bytes over UART -void UART::put(void* buffer, int numBytes) -{ - uint8_t* ptr = (uint8_t*) buffer; - while (numBytes > 0) { - int n = jtagatlantic_write(jtag, (char*) ptr, numBytes); - if (n < 0) { - fprintf(stderr, "Error writing to JTAG UART\n"); - exit(EXIT_FAILURE); - } - ptr += n; - numBytes -= n; - } -} - -// Is a byte available for reading? -bool UART::canGet() -{ - return jtagatlantic_bytes_available(jtag) > 0; -} - -// Receive bytes over UART -void UART::get(void* buffer, int numBytes) -{ - uint8_t* ptr = (uint8_t*) buffer; - while (numBytes > 0) { - int n = jtagatlantic_read(jtag, (char*) ptr, numBytes); - if (n < 0) { - fprintf(stderr, "Error reading from JTAG UART\n"); - exit(EXIT_FAILURE); - } - ptr += n; - numBytes -= n; - } -} - -// Flush writes -void UART::flush() -{ - if (jtag != NULL) jtagatlantic_flush(jtag); -} - -// Close UART -void UART::close() -{ - if (jtag != NULL) jtagatlantic_close(jtag); -} - -#endif diff --git a/Source/HostLink/UART.h b/Source/HostLink/UART.h deleted file mode 100755 index 0b682300..00000000 --- a/Source/HostLink/UART.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _UART_H_ -#define _UART_H_ - -#include -#include -#include -#include "JtagAtlantic.h" - -class UART { - #ifdef SIMULATE - int sock; - #else - JTAGATLANTIC* jtag; - #endif - public: - int instanceId; - - // Constructor - UART(); - - // Open UART with given instance id - void open(int instId); - - // Send bytes over UART - void put(void* buffer, int numBytes); - - // Receive bytes over UART - void get(void* buffer, int numBytes); - - // Is a byte available for reading? - bool canGet(); - - // Flush writes - void flush(); - - // Close UART - void close(); -}; - -#endif diff --git a/Source/HostLink/driver/Makefile b/Source/HostLink/driver/Makefile deleted file mode 100755 index b9f91df3..00000000 --- a/Source/HostLink/driver/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -KERNEL_SRC_DIR ?= /lib/modules/$(shell uname -r)/build - -obj-m := dmabuffer.o - -all: - make -C $(KERNEL_SRC_DIR) M=$(PWD) modules - -clean: - make -C $(KERNEL_SRC_DIR) M=$(PWD) clean - diff --git a/Source/HostLink/driver/dmabuffer.c b/Source/HostLink/driver/dmabuffer.c deleted file mode 100755 index 5bff2555..00000000 --- a/Source/HostLink/driver/dmabuffer.c +++ /dev/null @@ -1,249 +0,0 @@ -// This is a cut-down version of Ichiro Kawazome's udmabuf driver -// (https://github.com/ikwzm/udmabuf). It allocates a number of DMA buffers -// using Linux's DMA API and allows these buffers to be mmapped to user space. -// One device file is created for each buffer. The physical address of a -// buffer (to be used by a device) can be obtained using an ioctl on the -// corresponding device file. The number of buffers, and their sizes, are -// compile-time options. - -/****************************************************************************** - * - * Copyright (C) 2015-2017 Ichiro Kawazome - * Copyright (C) 2017 Matthew Naylor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include - -// Constants -// ========= - -// Number of DMA buffers to create -// (Also the number of device files to create) -#define NUM_BUFFERS 4 - -// Size of each buffer in bytes -#define SIZE 1048576 - -// Types -// ===== - -struct dmabuffer_state_t { - struct device* device; - struct cdev cdev; - int cdev_valid; - dev_t device_number; - int size; - size_t alloc_size; - void* virt_addr; - dma_addr_t phys_addr; - u64 dma_mask; -}; - -// Global state -// ============ - -// Base device number -static dev_t dmabuffer_base_devnum; - -// Sys class -static struct class* dmabuffer_sys_class; - -// State for each device -static struct dmabuffer_state_t dmabuffer_state[NUM_BUFFERS]; - -// File operations -// =============== - -static int dmabuffer_file_open(struct inode *inode, struct file *file) -{ - struct dmabuffer_state_t* state; - - state = container_of(inode->i_cdev, struct dmabuffer_state_t, cdev); - file->private_data = state; - - return 0; -} - -static int dmabuffer_file_release(struct inode *inode, struct file *file) -{ - return 0; -} - -static long dmabuffer_file_ioctl(struct file *file, - unsigned int ioctl_num, unsigned long ioctl_param) -{ - u64* ptr = (u64*) ioctl_param; - struct dmabuffer_state_t* state = file->private_data; - put_user(state->phys_addr, ptr); - return 0; -} - -static int dmabuffer_file_mmap(struct file *file, struct vm_area_struct* vma) -{ - struct dmabuffer_state_t* state = file->private_data; - return dma_mmap_coherent(state->device, vma, state->virt_addr, - state->phys_addr, state->alloc_size); -} - -static const struct file_operations dmabuffer_fops = { - .owner = THIS_MODULE, - .open = dmabuffer_file_open, - .release = dmabuffer_file_release, - .unlocked_ioctl = dmabuffer_file_ioctl, - .mmap = dmabuffer_file_mmap, -}; - -// Module init & exit -// ================== - -static void __exit dmabuffer_module_exit(void) -{ - // Free components for each device - int d; - for (d = 0; d < NUM_BUFFERS; d++) { - struct dmabuffer_state_t* state = &dmabuffer_state[d]; - // Free buffer - if (state->virt_addr != NULL) - dma_free_coherent(state->device, state->alloc_size, - state->virt_addr, state->phys_addr); - // Free device - if (state->device != NULL) - device_destroy(dmabuffer_sys_class, state->device_number); - // Remove char device - if (state->cdev_valid) - cdev_del(&state->cdev); - } - - // Free sys class - if (dmabuffer_sys_class != NULL) - class_destroy(dmabuffer_sys_class); - - // Free device numbers - if (dmabuffer_base_devnum != 0) - unregister_chrdev_region(dmabuffer_base_devnum, NUM_BUFFERS); -} - -static int __init dmabuffer_module_init(void) -{ - int retval, d, dma_mask_bit; - dev_t major; - - // Allocate device numbers - retval = alloc_chrdev_region(&dmabuffer_base_devnum, 0, - NUM_BUFFERS, "dmabuffer"); - if (retval != 0) { - printk(KERN_ERR "dmabuffer: couldn't allocate device numbers\n"); - dmabuffer_base_devnum = 0; - return retval; - } - - // Extract major device number - major = MAJOR(dmabuffer_base_devnum); - - // Create device class - dmabuffer_sys_class = class_create(THIS_MODULE, "dmabuffer"); - if (IS_ERR_OR_NULL(dmabuffer_sys_class)) { - printk(KERN_ERR "dmabuffer: couldn't create sys class\n"); - dmabuffer_sys_class = NULL; - dmabuffer_module_exit(); - return PTR_ERR(dmabuffer_sys_class); - } - - // Initialise each device - for (d = 0; d < NUM_BUFFERS; d++) { - struct dmabuffer_state_t* state = &dmabuffer_state[d]; - - // Set device number - state->device_number = MKDEV(major, d); - - // Compute size of allocation - state->alloc_size = - ((SIZE + ((1 << PAGE_SHIFT) - 1)) >> PAGE_SHIFT) << PAGE_SHIFT; - - // Create device - state->device = device_create(dmabuffer_sys_class, - NULL, state->device_number, - (void*) state, "dmabuffer%d", d); - if (IS_ERR_OR_NULL(state->device)) { - state->device = NULL; - dmabuffer_module_exit(); - return -1; - } - - // Setup DMA mask - dma_mask_bit = 32; - state->device->dma_mask = &state->dma_mask; - if (dma_set_mask(state->device, DMA_BIT_MASK(dma_mask_bit)) == 0) { - dma_set_coherent_mask(state->device, DMA_BIT_MASK(dma_mask_bit)); - } else { - printk(KERN_WARNING "dma_set_mask(DMA_BIT_MASK(%d)) failed\n", - dma_mask_bit); - dma_set_mask(state->device, DMA_BIT_MASK(32)); - dma_set_coherent_mask(state->device, DMA_BIT_MASK(32)); - } - - // Allocate buffer - state->virt_addr = - dma_alloc_coherent(state->device, state->alloc_size, - &state->phys_addr, GFP_KERNEL); - if (IS_ERR_OR_NULL(state->virt_addr)) { - printk(KERN_ERR "dma_alloc_coherent() failed\n"); - state->virt_addr = NULL; - dmabuffer_module_exit(); - return -1; - } - - // Initialise char device structure - cdev_init(&state->cdev, &dmabuffer_fops); - state->cdev.owner = THIS_MODULE; - - // Create char device - retval = cdev_add(&state->cdev, state->device_number, 1); - if (retval != 0) { - printk(KERN_ERR "dmabuffer: couldn't create devices\n"); - dmabuffer_module_exit(); - return retval; - } - state->cdev_valid = 1; - } - - return 0; -} - -module_init(dmabuffer_module_init); -module_exit(dmabuffer_module_exit); - -MODULE_LICENSE("Dual BSD/GPL"); diff --git a/Source/HostLink/pciestreamd.cpp b/Source/HostLink/pciestreamd.cpp deleted file mode 100755 index e719afbb..00000000 --- a/Source/HostLink/pciestreamd.cpp +++ /dev/null @@ -1,445 +0,0 @@ -// Copyright (c) Matthew Naylor - -// PCIeStream Daemon -// ================= -// -// Connect UNIX domain socket to FPGA FIFO via PCIeStream. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "PowerLink.h" - -// Constants -// --------- - -// Default socket location -#define SOCKET_NAME "pciestream" - -// Size of each DMA buffer in bytes -#define DMABufferSize 1048576 - -// Number of bytes per cache line -#define CacheLineBytes 64 - -// PCIeStream CSRs -#define CSR_ADDR_RX_A 0 -#define CSR_ADDR_RX_B 1 -#define CSR_ADDR_TX_A 2 -#define CSR_ADDR_TX_B 3 -#define CSR_LEN_RX_A 4 -#define CSR_LEN_RX_B 5 -#define CSR_LEN_TX_A 6 -#define CSR_LEN_TX_B 7 -#define CSR_EN 8 -#define CSR_INFLIGHT 10 -#define CSR_RESET 11 - -// Helper functions -// ---------------- - -// Memory barrier -inline void mfence() -{ - asm volatile("mfence"); -} - -// Flush cache line -static inline void clflush(volatile void *p) -{ - asm volatile("clflush %0" : "+m" (*(volatile char *) p)); -} - -// Return minimum of two integers -inline int min(int x, int y) -{ - return x < y ? x : y; -} - -// Swap pointers -void swap(volatile char** p, volatile char** q) -{ - volatile char* tmp = *p; *p = *q; *q = tmp; -} - -// Check if connection is alive -int alive(int sock) -{ - //char buf; - //int ret = recv(sock, &buf, 1, MSG_PEEK | MSG_DONTWAIT); - //int closed = ret == 0 || (ret == -1 && errno != EAGAIN); - char buf; - return send(sock, &buf, 0, 0) == 0; -} - -// Types -// ----- - -// Return value of transmitter or receiver step function -typedef enum { - CLOSED, // Client has closed the connection - FULL, // Transmit buffer is full - EMPTY, // Receive buffer is empty - NO_SEND, // Nothing to send from client - CLIENT_BUSY, // Client can't currently receive more data - PROGRESS // Progress was made -} Status; - -// Transmitter state -typedef struct { - // Access to control/status registers on FPGA side - volatile uint64_t* csrs; - // Transmit buffer A - volatile char* txA; - // Transmit buffer B - volatile char* txB; - // Client connection (socket) - int client; - // Which buffer in the double buffer is currently being written to? - int activeBuffer; - // Is the active buffer ready to be filled? - int bufferReady; - // The number of bytes written to the active buffer but not yet sent - int pending; -} TxState; - -// Receiver state -typedef struct { - // Access to control/status registers on FPGA side - volatile uint64_t* csrs; - // Receive buffer A - volatile char* rxA; - // Receive buffer B - volatile char* rxB; - // Client connection (socket) - int client; - // Which buffer in the double buffer is currently being read from? - int activeBuffer; - // Number of bytes written to the client - int written; - // The number of bytes in the DMA buffer available for reading - int available; -} RxState; - -// Transmitter -// ----------- - -// Intialise the transmitter state -void txInit(TxState* s, int client, volatile uint64_t* csrs, - volatile char* txA, volatile char* txB) -{ - s->client = client; - s->csrs = csrs; - s->txA = txA; - s->txB = txB; - s->activeBuffer = 0; - s->bufferReady = 0; - s->pending = 0; -} - -// Read from socket and write to FPGA -Status tx(TxState* s) -{ - // This flag indicates that data should now be sent - int doSend = 0; - - // Send pending data if buffer is full - if (s->pending == DMABufferSize) doSend = 1; - - // Send pending data if: (1) pending is a non-zero multiple of - // 16 and (2) there's no data available on the socket. - if (s->pending != 0 && (s->pending&0xf) == 0) { - struct pollfd fd; fd.fd = s->client; fd.events = POLLIN; - int ret = poll(&fd, 1, 0); - if (ret <= 0) doSend = 1; - } - - // Try to read data from client - if (! doSend) { - // Determine if DMA buffer is available - if (! s->bufferReady) { - if (s->csrs[2*(CSR_LEN_TX_A + s->activeBuffer)] == 0) - s->bufferReady = 1; - else - return FULL; - } - // Is there any data to transmit? - struct pollfd fd; fd.fd = s->client; fd.events = POLLIN; - int ret = poll(&fd, 1, 0); - if (ret == 0) - return NO_SEND; - else if (ret < 0) - return CLOSED; - else { - // Read data from client - int n = read(s->client, (void*) &s->txA[s->pending], - DMABufferSize - s->pending); - if (n <= 0) return CLOSED; - s->pending += n; - } - } - - // Send pending data, if requested - if (doSend) { - // Flush cache - mfence(); - for (int i = 0; i < s->pending; i += CacheLineBytes) clflush(&s->txA[i]); - mfence(); - // Trigger send - assert(s->bufferReady && s->pending >= 16); - s->csrs[2*(CSR_LEN_TX_A + s->activeBuffer)] = s->pending/16; - // Switch buffers - swap(&s->txA, &s->txB); - s->activeBuffer = (s->activeBuffer+1)&1; - s->pending = 0; - s->bufferReady = 0; - } - - return PROGRESS; -} - -// Receiver -// -------- - -// Initialise the receiver state -void rxInit(RxState* s, int client, volatile uint64_t* csrs, - volatile char* rxA, volatile char* rxB) -{ - s->client = client; - s->csrs = csrs; - s->rxA = rxA; - s->rxB = rxB; - s->activeBuffer = 0; - s->written = 0; - s->available = 0; -} - -// Read from FPGA and write to socket -Status rx(RxState* s) -{ - // Determine if data is available to receive - if (s->available == 0) { - s->available = 16 * s->csrs[2*(CSR_LEN_RX_A + s->activeBuffer)]; - if (s->available == 0) - return EMPTY; - // Cache flush - for (int i = 0; i < s->available; i += CacheLineBytes) clflush(&s->rxA[i]); - mfence(); - } - - // Can we send data to the client? - struct pollfd fd; fd.fd = s->client; fd.events = POLLOUT; - int ret = poll(&fd, 1, 0); - if (ret == 0) - return CLIENT_BUSY; - else if (ret < 0) - return CLOSED; - else { - // Write data to socket - int n = write(s->client, (void*) &s->rxA[s->written], - s->available - s->written); - if (n <= 0) return CLOSED; - s->written += n; - - // Consume data from FPGA - if (s->written == s->available) { - // Make sure all the reads are done before the next write - mfence(); - // Finished with this buffer - s->csrs[2*(CSR_LEN_RX_A + s->activeBuffer)] = 0; - // Switch buffers - swap(&s->rxA, &s->rxB); - s->activeBuffer = (s->activeBuffer+1)&1; - s->available = s->written = 0; - } - } - - return PROGRESS; -} - -// Main function -// ------------- - -// Display usage and quit -void usage() -{ - fprintf(stderr, "Usage: pciestreamd [BAR0]\n" - "Where BAR0 is a physical address in hex\n"); - exit(EXIT_FAILURE); -} - -// Open a DMA buffer -volatile char* openDMABuffer(const char* deviceFile, int prot, uint64_t* addr) -{ - int dev = open(deviceFile, O_RDWR); - if (dev == -1) - { - perror("open DMA buffer"); - exit(EXIT_FAILURE); - } - - if (ioctl(dev, 0, addr) == -1) { - perror("ioctl DMA buffer"); - exit(EXIT_FAILURE); - } - - void* ptr = - mmap(NULL, - 0x100000, - prot, - MAP_SHARED, - dev, - 0); - - if (ptr == MAP_FAILED) { - perror("mmap DMA buffer"); - exit(EXIT_FAILURE); - } - - return (volatile char*) ptr; -} - -// Create listening socket -int createListener() -{ - // Create socket - int sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock == -1) { - perror("pciestreamd: socket"); - exit(EXIT_FAILURE); - } - - // Bind socket - struct sockaddr_un sockAddr; - memset(&sockAddr, 0, sizeof(struct sockaddr_un)); - sockAddr.sun_family = AF_UNIX; - sockAddr.sun_path[0] = '\0'; - strncpy(&sockAddr.sun_path[1], SOCKET_NAME, - sizeof(sockAddr.sun_path)-2); - int ret = bind(sock, (const struct sockaddr *) &sockAddr, - sizeof(struct sockaddr_un)); - if (ret == -1) { - perror("pciestreamd: bind"); - exit(EXIT_FAILURE); - } - - // Listen for connections - ret = listen(sock, 0); - if (ret == -1) { - perror("Control: listen"); - exit(EXIT_FAILURE); - } - - return sock; -} - -int main(int argc, char* argv[]) -{ - if (argc != 2) usage(); - - uint64_t ctrlBAR; - if (sscanf(argv[1], "%lx", &ctrlBAR) <= 0) usage(); - - // Ignore SIGPIPE - signal(SIGPIPE, SIG_IGN); - - // Obtain access to control BAR - // ---------------------------- - - int memDev = open("/dev/mem", O_RDWR); - if (memDev == -1) - { - perror("open /dev/mem"); - exit(EXIT_FAILURE); - } - - void *csrsPtr = - mmap(NULL, - 0x40000, - PROT_READ | PROT_WRITE, - MAP_SHARED, - memDev, - ctrlBAR); - - if (csrsPtr == MAP_FAILED) { - perror("mmap csrs"); - exit(EXIT_FAILURE); - } - - volatile uint64_t* csrs = (uint64_t*) csrsPtr; - - // Obtain access to DMA buffers - // ---------------------------- - - uint64_t addrRxA, addrRxB, addrTxA, addrTxB; - - volatile char* rxA = openDMABuffer("/dev/dmabuffer0", PROT_READ, &addrRxA); - volatile char* rxB = openDMABuffer("/dev/dmabuffer1", PROT_READ, &addrRxB); - volatile char* txA = openDMABuffer("/dev/dmabuffer2", PROT_WRITE, &addrTxA); - volatile char* txB = openDMABuffer("/dev/dmabuffer3", PROT_WRITE, &addrTxB); - - // Main loop - // --------- - - // Create listener socket - int sock = createListener(); - - // Transmitter and receiver state - TxState txState; - RxState rxState; - - for (;;) { - // Accept connection - int conn = accept(sock, NULL, NULL); - if (conn == -1) { - perror("pciestreamd: accept"); - exit(EXIT_FAILURE); - } - - // Reset PCIeStream hardware - csrs[2*CSR_EN] = 0; - while (csrs[2*CSR_INFLIGHT] != 0); - csrs[2*CSR_RESET] = 1; - usleep(100000); - csrs[2*CSR_ADDR_RX_A] = addrRxA; - csrs[2*CSR_ADDR_RX_B] = addrRxB; - csrs[2*CSR_ADDR_TX_A] = addrTxA; - csrs[2*CSR_ADDR_TX_B] = addrTxB; - csrs[2*CSR_EN] = 1; - - // Reset state - txInit(&txState, conn, csrs, txA, txB); - rxInit(&rxState, conn, csrs, rxA, rxB); - - // Event loop - for (;;) { - Status txStatus = tx(&txState); - if (txStatus == CLOSED) break; - Status rxStatus = rx(&rxState); - if (rxStatus == CLOSED) break; - if (txStatus != PROGRESS && rxStatus != PROGRESS) { - if (! alive(conn)) break; - usleep(100); - } - } - - close(conn); - - // Power down FPGAs - powerEnable(0); - } - - return 0; -} diff --git a/Source/HostLink/tinsel-power.cpp b/Source/HostLink/tinsel-power.cpp deleted file mode 100755 index abbd846f..00000000 --- a/Source/HostLink/tinsel-power.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include -#include -#include "PowerLink.h" - -void usage() -{ - printf("tinsel-power (on|off)\n"); - exit(EXIT_FAILURE); -} - -int main(int argc, char* argv[]) -{ - if (argc < 2) usage(); - - if (! strcmp(argv[1], "on")) { - powerEnable(1); - } - else if (! strcmp(argv[1], "off")) { - powerEnable(0); - } - else { - usage(); - } - - return 0; -} diff --git a/Source/HostLink/udsock.c b/Source/HostLink/udsock.c deleted file mode 100755 index 3d042b57..00000000 --- a/Source/HostLink/udsock.c +++ /dev/null @@ -1,114 +0,0 @@ -// Command-line utilities for UNIX Domain Sockets - -#include -#include -#include -#include -#include -#include -#include -#include - - -void usage() -{ - printf("Connect two UNIX Domain Sockets:\n" - " udsock join [SOCKET] [SOCKET]\n\n" - "Connect UNIX Domain Socket to stdin:\n" - " udsock in [SOCKET]\n\n" - "Connect UNIX Domain Socket to stdout:\n" - " udsock out [SOCKET]\n\n" - "Connect UNIX Domain Socket to stdin and stdout:\n" - " udsock inout [SOCKET]\n\n"); - exit(EXIT_FAILURE); -} - -void join(int out, int in) -{ - char buffer[65536]; - - for (;;) { - // Read from 'in' - int n = read(in, buffer, sizeof(buffer)); - if (n <= 0) return; - // Write to 'out' - int i = 0; - while (n > 0) { - int m = write(out, &buffer[i], n); - if (m <= 0) return; - n = n-m; - i = i+m; - } - } -} - -int main(int argc, char* argv[]) -{ - // Ignore SIGPIPE - signal(SIGPIPE, SIG_IGN); - - int sock1 = -1; - int sock2 = -1; - int in = -1; - int out = -1; - - if (argc < 3) usage(); - - sock1 = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock1 == -1) { - perror("socket"); - return -1; - } - - // Connect to socket - struct sockaddr_un addr, addr2; - memset(&addr, 0, sizeof(struct sockaddr_un)); - memset(&addr2, 0, sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, argv[2], sizeof(addr.sun_path)-1); - for (int i = 0; i < strlen(addr.sun_path); i++) - if (addr.sun_path[i] == '@') addr.sun_path[i] = '\0'; - while (connect(sock1, (struct sockaddr *) &addr, - sizeof(struct sockaddr_un)) < 0) sleep(1); - - if (argc == 4) { - if (!strcmp(argv[1], "join")) { - sock2 = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock2 == -1) { - perror("socket"); - exit(EXIT_FAILURE); - } - - // Connect to socket - addr2.sun_family = AF_UNIX; - strncpy(addr2.sun_path, argv[3], sizeof(addr2.sun_path)-1); - for (int i = 0; i < strlen(addr2.sun_path); i++) - if (addr2.sun_path[i] == '@') addr2.sun_path[i] = '\0'; - while (connect(sock2, (struct sockaddr *) &addr2, - sizeof(struct sockaddr_un)) < 0) sleep(1); - } - else - usage(); - if (fork() == 0) - join(sock2, sock1); - else - join(sock1, sock2); - } - else if (argc == 3) { - if (!strcmp(argv[1], "in")) - join(sock1, STDIN_FILENO); - else if (!strcmp(argv[1], "out")) - join(STDOUT_FILENO, sock1); - else if (!strcmp(argv[1], "inout")) { - if (fork() == 0) - join(STDOUT_FILENO, sock1); - else - join(sock1, STDIN_FILENO); - } - else - usage(); - } - else usage(); - - return 0; -} diff --git a/Source/Mothership/Mothership.cpp b/Source/Mothership/Mothership.cpp index 47ba9878..0a2f2e31 100644 --- a/Source/Mothership/Mothership.cpp +++ b/Source/Mothership/Mothership.cpp @@ -74,9 +74,11 @@ printf("********* Mothership rank %d on the way out\n",Urank); fflush(stdout); //------------------------------------------------------------------------------ Mothership::~Mothership() +// Destructor gets rid of response pins added by the Twig, tasks, and +// additional event maps for comms other than MPI_COMM_WORLD { -DebugPrint("********* Mothership rank %d destructor\n",Urank); -WALKMAP(uint32_t,PinBuf_t*,TwigMap,D) +DebugPrint("********* Mothership rank %d destructor\n",Urank); fflush(stdout); +WALKMAP(uint32_t,PinBuf_t*,TwigMap,D) // get rid of Twig pins { if (D->second) { @@ -85,19 +87,27 @@ WALKMAP(uint32_t,PinBuf_t*,TwigMap,D) delete D->second; } } -WALKMAP(string,TaskInfo_t*,TaskMap,T) +WALKMAP(string,TaskInfo_t*,TaskMap,T) // and deployed tasks delete T->second; WALKVECTOR(FnMap_t*,FnMapx,F) delete *F; // get rid of derived class function tables +DebugPrint("********* Mothership rank %d destructor finished\n",Urank); +fflush(stdout); } //------------------------------------------------------------------------------ unsigned Mothership::Boot(string task) +/* Boot handles 2 functions: starting the cores and then dealing with a software + barrier at startup. For the moment, the Tinsel barrier is not thread-safe, + so this has to be executed at the level of the main mothership process. + (We might prefer [PIP?] a thread-safe start process that can take a vector of + cores and boot them all in parallel) +*/ { DebugPrint("Entering boot stage\n"); - if (TaskMap.find(task) == TaskMap.end()) + if (TaskMap.find(task) == TaskMap.end()) // no such task { Post(107,task); return 1; @@ -105,7 +115,7 @@ unsigned Mothership::Boot(string task) DebugPrint("Task %s being booted\n", task.c_str()); switch(TaskMap[task]->status) { - case TaskInfo_t::TASK_BOOT: + case TaskInfo_t::TASK_BOOT: // already being booted. This should not be run again. { Post(513, task, TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); TaskMap[task]->status = TaskInfo_t::TASK_ERR; @@ -126,9 +136,16 @@ unsigned Mothership::Boot(string task) if (numThreads) { (*C)->get_hardware_address()->populate_a_software_address(&coreAddress); - t_start_bitmap[Mothership::GetHWAddr(coreAddress)] = new vector(numThreads/(8*sizeof(unsigned)),UINT_MAX); + // the bitmap is a vector of unsigneds, each bit representing a + // thread to be started. We can set up all threads in the bitmap + // below the last modulo-32 group by simply initialising a vector of + // FFFFFFFF's + t_start_bitmap[Mothership::GetHWAddr(coreAddress)] = + new vector(numThreads/(8*sizeof(unsigned)),UINT_MAX); unsigned remainder = numThreads%(8*sizeof(unsigned)); - if (remainder) t_start_bitmap[Mothership::GetHWAddr(coreAddress)]->push_back(UINT_MAX >> ((8*sizeof(unsigned))-remainder)); + if (remainder) + t_start_bitmap[Mothership::GetHWAddr(coreAddress)]->push_back + (UINT_MAX >> ((8*sizeof(unsigned))-remainder)); } } DebugPrint("Task start bitmaps created for %d cores\n", t_start_bitmap.size()); @@ -137,10 +154,16 @@ unsigned Mothership::Boot(string task) { (*C)->get_hardware_address()->populate_a_software_address(&coreAddress); fromAddr(Mothership::GetHWAddr(coreAddress),&mX,&mY,&core,&thread); - DebugPrint("Booting %d threads on threadID 0x%X at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress),mX,mY,core); + DebugPrint("Booting %d threads on threadID 0x%X at x:%d y:%d c:%d\n", + (*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress), + mX,mY,core); startOne(mX,mY,core,(*C)->P_threadm.size()); - DebugPrint("%d threads started on threadID 0x%X at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress),mX,mY,core); - DebugPrint("Triggering %d threads on core %d at x:%d y:%d c:%d\n",(*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress),mX,mY,core); + DebugPrint("%d threads started on threadID 0x%X at x:%d y:%d c:%d\n", + (*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress), + mX,mY,core); + DebugPrint("Triggering %d threads on core %d at x:%d y:%d c:%d\n", + (*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress), + mX,mY,core); goOne(mX,mY,core); } // per Matt Naylor comment safer to start all cores then issue the go command to all cores separately. @@ -151,30 +174,49 @@ unsigned Mothership::Boot(string task) // goOne(mX,mY,core); // } // DebugPrint("%d cores booted\n", taskCores.size()); + + // deal with the barrier. Keep receiving messages until the bitmap is clear. P_Sup_Msg_t barrier_msg; while (!t_start_bitmap.empty()) { - recvMsg(&barrier_msg, p_sup_hdr_size()); + recvMsg(&barrier_msg, p_sup_hdr_size()); // blocking receive message call DebugPrint("Received a message from a core during application barrier\n"); - if ((barrier_msg.header.sourceDeviceAddr & P_SUP_MASK) && (barrier_msg.header.command == P_PKT_MSGTYP_BARRIER)) + if ((barrier_msg.header.sourceDeviceAddr & P_SUP_MASK) && + (barrier_msg.header.command == P_PKT_MSGTYP_BARRIER)) { - DebugPrint("Barrier message from thread ID 0x%X\n", barrier_msg.header.sourceDeviceAddr ^ P_SUP_MASK); - fromAddr(((barrier_msg.header.sourceDeviceAddr ^ P_SUP_MASK) >> P_THREAD_OS),&mX,&mY,&core,&thread); + DebugPrint("Barrier message from thread ID 0x%X\n", + barrier_msg.header.sourceDeviceAddr ^ P_SUP_MASK); + fromAddr(((barrier_msg.header.sourceDeviceAddr ^ P_SUP_MASK) >> P_THREAD_OS), + &mX,&mY,&core,&thread); unsigned hw_core = toAddr(mX,mY,core,0); DebugPrint("Received a barrier acknowledge from core %#X\n", hw_core); - if (t_start_bitmap.find(hw_core) != t_start_bitmap.end()) + // valid core? + if (t_start_bitmap.find(hw_core) != t_start_bitmap.end()) { DebugPrint("Thread %d on core %d responding\n", thread, core); - DebugPrint("Core bitmap for thread %d before acknowledge: %#X\n", thread, (*t_start_bitmap[hw_core])[thread/(8*sizeof(unsigned))]); - (*t_start_bitmap[hw_core])[thread/(8*sizeof(unsigned))] &= (~(1 << (thread%(8*sizeof(unsigned))))); - DebugPrint("Core bitmap for thread %d after acknowledge: %#X\n", thread, (*t_start_bitmap[hw_core])[thread/(8*sizeof(unsigned))]); + DebugPrint("Core bitmap for thread %d before acknowledge: %#X\n", + thread, + (*t_start_bitmap[hw_core])[thread/(8*sizeof(unsigned))]); + // clear the bitmap bit + (*t_start_bitmap[hw_core])[thread/(8*sizeof(unsigned))] &= + (~(1 << (thread%(8*sizeof(unsigned))))); + DebugPrint("Core bitmap for thread %d after acknowledge: %#X\n", + thread, + (*t_start_bitmap[hw_core])[thread/(8*sizeof(unsigned))]); vector::iterator S; - DebugPrint("Core bitmap currently has %d elements\n", t_start_bitmap.size()); - for (S = t_start_bitmap[hw_core]->begin(); S != t_start_bitmap[hw_core]->end(); S++) if (*S) break; - DebugPrint("Core bitmap for core %d has %d subelements\n", hw_core, t_start_bitmap[hw_core]->size()); + DebugPrint("Core bitmap currently has %d elements\n", + t_start_bitmap.size()); + // any elements of the vector still have uncleared bits? + for (S = t_start_bitmap[hw_core]->begin(); + S != t_start_bitmap[hw_core]->end(); S++) if (*S) break; + DebugPrint("Core bitmap for core %d has %d subelements\n", + hw_core, t_start_bitmap[hw_core]->size()); + // all clear. This core is good to go. Remove it from the + // bitmap vector. if (S == t_start_bitmap[hw_core]->end()) { - DebugPrint("Removing core bitmap for core %d\n", thread, t_start_bitmap[hw_core]->size()); + DebugPrint("Removing core bitmap for core %d\n", + thread, t_start_bitmap[hw_core]->size()); t_start_bitmap[hw_core]->clear(); delete t_start_bitmap[hw_core]; t_start_bitmap.erase(hw_core); @@ -182,26 +224,35 @@ unsigned Mothership::Boot(string task) } } } + // Through the barrier. DebugPrint("%d cores passed Mothership barrier and awaiting start signal \n", taskCores.size()); // MPI_Barrier(Comms[0]); // barrier on the mothercore side (temporarily removed until we have multi-mothership systems) - // create a thread (later will be a process) to deal with packets from - // tinsel cores. Have to do it here, after all the initial setup is complete, - // otherwise setup barrier communications may fail. void* args = this; // at this point, load the Supervisor for the task. - SuperHandle = dlopen((TaskMap[task]->BinPath+"/libSupervisor.so").c_str(), RTLD_NOW); - if (!SuperHandle) Post(532,(TaskMap[task]->BinPath+"/libSupervisor.so"),int2str(Urank),string(dlerror())); + SuperHandle = dlopen((TaskMap[task]->BinPath+"/libSupervisor.so").c_str(), + RTLD_NOW); + if (!SuperHandle) Post(532,(TaskMap[task]->BinPath+"/libSupervisor.so"), + int2str(Urank),string(dlerror())); else { Post (540,int2str(Urank)); //int (*SupervisorInit)(CommonBase*) = reinterpret_cast(dlsym(SuperHandle, "SupervisorInit")); - int (*SupervisorInit)() = reinterpret_cast(dlsym(SuperHandle, "SupervisorInit")); + // set up initialiser function + int (*SupervisorInit)() = reinterpret_cast(dlsym(SuperHandle, + "SupervisorInit")); string badFunc(""); //if (!SupervisorInit || (*SupervisorInit)(this)) badFunc = "SupervisorInit"; + // run the initialiser if (!SupervisorInit || (*SupervisorInit)()) badFunc = "SupervisorInit"; - else if ((SupervisorCall = reinterpret_cast(dlsym(SuperHandle, "SupervisorCall"))) == NULL) badFunc = "SupervisorCall"; + // and set up the main Supervisor function + else if ((SupervisorCall = reinterpret_cast + (dlsym(SuperHandle, "SupervisorCall"))) == NULL) + badFunc = "SupervisorCall"; if (badFunc.size()) Post(533,badFunc,int2str(Urank),string(dlerror())); } + // create a thread (later will be a process) to deal with packets from + // tinsel cores. Have to do it here, after all the initial setup is complete, + // otherwise setup barrier communications may fail. ForwardMsgs = true; // set forwarding on so thread doesn't immediately exit if (pthread_create(&Twig_thread,NULL,Twig,args)) { @@ -228,45 +279,75 @@ unsigned Mothership::Boot(string task) //------------------------------------------------------------------------------ unsigned Mothership::CmLoad(string task) -// Load a task to the system +/* Load a task to the system. The task needs to have been previously deployed. + This function responds to the task /init command (or equivalent). It farms + off loading of individual boards to the LoadBoard function. LoadBoard CAN be + threaded, but current Tinsel implementations don't support it, so for the + moment LoadBoard works (slowly!) one board at a time. +*/ { DebugPrint("%d tasks deployed to Mothership\n", TaskMap.size()); - if (TaskMap.find(task) == TaskMap.end()) + if (TaskMap.find(task) == TaskMap.end()) // task deployed? { - Post(515, task, int2str(Urank)); + Post(515, task, int2str(Urank)); // No. Abandon. return 0; } DebugPrint("Task %s found\n", task.c_str()); WALKMAP(string,TaskInfo_t*,TaskMap,T) { - // only one task can be active at a time (for the moment. Later we may want more sophisticated task mapping - // that allows multiple active tasks on non-overlapping board sets) - if ((T->first != task) && (T->second->status & (TaskInfo_t::TASK_BOOT | TaskInfo_t::TASK_RDY | TaskInfo_t::TASK_BARR | TaskInfo_t::TASK_RUN | TaskInfo_t::TASK_STOP | TaskInfo_t::TASK_ERR))) + // only one task can be active at a time (for the moment. Later we may + // want more sophisticated task mapping that allows multiple active tasks + // on non-overlapping board sets). Look for other active tasks. + if ((T->first != task) && (T->second->status & (TaskInfo_t::TASK_BOOT | + TaskInfo_t::TASK_RDY | + TaskInfo_t::TASK_BARR | + TaskInfo_t::TASK_RUN | + TaskInfo_t::TASK_STOP | + TaskInfo_t::TASK_ERR))) { - map::const_iterator othStatus = TaskInfo_t::Task_Status.find(T->second->status); + // found another task seemingly active. What state is it in? + map::const_iterator othStatus = + TaskInfo_t::Task_Status.find(T->second->status); if (othStatus == TaskInfo_t::Task_Status.end()) - T->second->status = TaskInfo_t::TASK_ERR; + T->second->status = TaskInfo_t::TASK_ERR; // status corrupted. Post(508,T->first); return 2; + // other task was in a sane but active state. Abandon. Post(509,task,T->first,othStatus->second); return 0; } } DebugPrint("Task status confirmed as TASK_LOAD\n"); - if (!((TaskMap[task]->status == TaskInfo_t::TASK_IDLE) || (TaskMap[task]->status == TaskInfo_t::TASK_END))) + // Only this task is active. But since it's already active it + // doesn't need to be loaded. + if (!((TaskMap[task]->status == TaskInfo_t::TASK_IDLE) || + (TaskMap[task]->status == TaskInfo_t::TASK_END))) { - Post(511,task,"loaded to hardware",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + Post(511,task,"loaded to hardware", + TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); return 0; } + // otherwise we are good to go. Task goes to boot state. TaskMap[task]->status = TaskInfo_t::TASK_BOOT; DebugPrint("Task status is TASK_BOOT\n"); + // Now load the boards. For the moment one at a time, later + // this will be threaded. long coresThisTask = 0; long coresLoaded = 0; + long boardsLoaded = 0; + unsigned totalBoards = TaskMap[task]->BoardsForTask().size(); + unsigned totalCores = TaskMap[task]->CoresForTask().size(); DebugPrint("Task will use %ld boards.\n", - TaskMap[task]->BoardsForTask().size()); + totalBoards); // for each board mapped to the task, WALKVECTOR(P_board*,TaskMap[task]->BoardsForTask(),B) { + // some output is useful here to keep the operator aware that + // we're doing something + Post(563, long2str(coresLoaded), + uint2str(totalCores), + uint2str(boardsLoaded), + uint2str(totalBoards)); DebugPrint("Board \"%s\" will use %ld mailboxes.\n", (*B)->Name().c_str(), (*B)->G.SizeNodes()); WALKPDIGRAPHNODES(AddressComponent, P_mailbox*, @@ -281,6 +362,7 @@ unsigned Mothership::CmLoad(string task) coresThisTask += (*B)->G.NodeData(MB)->P_corem.size(); } coresLoaded += LoadBoard(*B); // load the board (unthreaded model) + ++boardsLoaded; } DebugPrint("All boards finished. %ld cores were in the hardware model for " "this task. %ld cores have been loaded.\n", @@ -304,17 +386,17 @@ unsigned Mothership::CmLoad(string task) unsigned Mothership::CmRun(string task) // Run a specified task. This passes the tinsel-side barrier to start -// application execution +// application execution. Invoked using task /run or equivalent. { - if (TaskMap.find(task) == TaskMap.end()) + if (TaskMap.find(task) == TaskMap.end()) // task exists? { Post(107, task); return 0; } - switch(TaskMap[task]->status) + switch(TaskMap[task]->status) // and is it ready to run? { - case TaskInfo_t::TASK_BOOT: - case TaskInfo_t::TASK_RDY: + case TaskInfo_t::TASK_BOOT: // these states mean it's still booting. + case TaskInfo_t::TASK_RDY: // i.e. not ready. { Post(513, task, TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); TaskMap[task]->status = TaskInfo_t::TASK_ERR; @@ -325,7 +407,7 @@ unsigned Mothership::CmRun(string task) TaskMap[task]->status = TaskInfo_t::TASK_ERR; return 2; case TaskInfo_t::TASK_END: - case TaskInfo_t::TASK_IDLE: + case TaskInfo_t::TASK_IDLE: // active or not deployed case TaskInfo_t::TASK_RUN: Post(511, task,"run",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); return 0; @@ -334,11 +416,12 @@ unsigned Mothership::CmRun(string task) DebugPrint("Task %s entering tinsel barrier\n",task.c_str()); P_Msg_Hdr_t barrier_msg; barrier_msg.messageLenBytes = p_hdr_size(); // barrier is only a header. No payload. - barrier_msg.destEdgeIndex = 0; // no edge index necessary. - barrier_msg.destPin = P_SUP_PIN_INIT; // it goes to the system __init__ pin - barrier_msg.messageTag = P_MSG_TAG_INIT; // and is of message type __init__. + barrier_msg.destEdgeIndex = 0; // no edge index necessary. + barrier_msg.destPin = P_SUP_PIN_INIT; // it goes to the system __init__ pin + barrier_msg.messageTag = P_MSG_TAG_INIT; // and is of message type __init__. DebugPrint("Building thread list for task %s\n",task.c_str()); - // build a list of the threads in this task (that should be released from barrier) + // build a list of the threads in this task (that should be released + // from barrier) vector threadsToRelease; P_addr threadAddress; WALKVECTOR(P_thread*,TaskMap[task]->ThreadsForTask(),R) @@ -352,10 +435,14 @@ unsigned Mothership::CmRun(string task) // and then issue the barrier release to the threads. WALKVECTOR(unsigned,threadsToRelease,R) { - barrier_msg.destDeviceAddr = DEST_BROADCAST; // send to every device on the thread with a supervisor message + // send to every device on the thread with a supervisor message + barrier_msg.destDeviceAddr = DEST_BROADCAST; DebugPrint("Attempting to send barrier release message to the thread " "with hardware address %u.\n", *R); - send(*R,(p_hdr_size()/(4 << TinselLogWordsPerFlit) + (p_hdr_size()%(4 << TinselLogWordsPerFlit) ? 1 : 0)), &barrier_msg, true); + // this is the 'magic invocation' to release the Tinsel barrier + send(*R,(p_hdr_size()/(4 << TinselLogWordsPerFlit) + + (p_hdr_size()%(4 << TinselLogWordsPerFlit) ? 1 : 0)), + &barrier_msg, true); } DebugPrint("Tinsel threads now on their own for task %s\n",task.c_str()); TaskMap[task]->status = TaskInfo_t::TASK_RUN; @@ -371,24 +458,28 @@ unsigned Mothership::CmRun(string task) //------------------------------------------------------------------------------ unsigned Mothership::CmStop(string task) -// Handle a stop command (which ends the simulation) +// Handle a task /stop command (which ends the simulation). Even if the task +// has internal termination detection, task /stop should always be issued to +// shut down the task cleanly (close Supervisors, clear memory, etc.) { - if (TaskMap.find(task) == TaskMap.end()) + if (TaskMap.find(task) == TaskMap.end()) // no such task. { Post(107, task); return 0; } - switch(TaskMap[task]->status) + switch(TaskMap[task]->status) // is task in a state ready to be stopped? { case TaskInfo_t::TASK_IDLE: case TaskInfo_t::TASK_END: - Post(511, task,"stopped",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + Post(511, task,"stopped", + TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); return 0; case TaskInfo_t::TASK_BOOT: case TaskInfo_t::TASK_RDY: case TaskInfo_t::TASK_STOP: { - Post(813, task, "stopped",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + Post(813, task, "stopped", + TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); return 1; } case TaskInfo_t::TASK_BARR: @@ -408,9 +499,9 @@ unsigned Mothership::CmStop(string task) TaskMap[task]->status = TaskInfo_t::TASK_STOP; // set up for shutdown by creating a global stop message P_Msg_Hdr_t stop_msg; - stop_msg.destEdgeIndex = 0; // ignore edge index. Unused. + stop_msg.destEdgeIndex = 0; // ignore edge index. Unused. stop_msg.destPin = P_SUP_PIN_SYS_SHORT; // goes to the system pin - stop_msg.messageTag = P_MSG_TAG_STOP; // with a stop message type + stop_msg.messageTag = P_MSG_TAG_STOP; // with a stop message type DebugPrint("Stopping task %s\n",task.c_str()); // go through each thread of the task, //vector threads_for_task = TaskMap[task]->ThreadsForTask(); @@ -421,38 +512,54 @@ unsigned Mothership::CmStop(string task) uint32_t destDevAddr = Mothership::GetHWAddr(threadAddress); DebugPrint("Stopping thread %d in task %s\n", destDevAddr, task.c_str()); stop_msg.destDeviceAddr = DEST_BROADCAST; // issue the stop message to all devices - // then issue the stop packet - send(destDevAddr,(p_hdr_size()/(4 << TinselLogWordsPerFlit) + p_hdr_size()%(4 << TinselLogWordsPerFlit) ? 1 : 0), &stop_msg, true); + // then issue the stop packet. Packet size 'future proofed' by computing + // it rather than relying on a static constant (this should compile down + // to a constant anyway) + send(destDevAddr,(p_hdr_size()/(4 << TinselLogWordsPerFlit) + + p_hdr_size()%(4 << TinselLogWordsPerFlit) ? 1 : 0), + &stop_msg, true); } TaskMap[task]->status = TaskInfo_t::TASK_END; // check to see if there are any other active tasks WALKMAP(string, TaskInfo_t*, TaskMap, tsk) - if ((tsk->second->status == TaskInfo_t::TASK_BARR) || (tsk->second->status == TaskInfo_t::TASK_RUN)) return 0; - // if not, shut down the Twig thread. + if ((tsk->second->status == TaskInfo_t::TASK_BARR) || + (tsk->second->status == TaskInfo_t::TASK_RUN)) return 0; + // if not, shut down the Twig thread. For the moment, this also closes + // supervisors. When we have multiple Supervisors running, it is probably + // going to be better to shut each one down here. if (twig_running) StopTwig(); return 0; } + // any unexpected states generate an error. default: TaskMap[task]->status = TaskInfo_t::TASK_ERR; } - Post(813,task,"stopped",TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); + Post(813,task,"stopped", + TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); return 2; } //------------------------------------------------------------------------------ unsigned Mothership::ConfigDistribute(PMsg_p* msg, unsigned comm) -// receive a broadcast core info block from the NameServer. +/* receive a broadcast core info block from the NameServer. This responds to + the task /deploy command. Deploying a task has 2 effects: 1) it populates + nameserver entries on this Mothership for the task; 2) it sets up a + TaskInfo_t structure which contains basic info about the task such as what + state it's in, where its files are, and what hardware resources it 'owns'. +*/ { - CMsg_p task_info(*msg); - string TaskName = msg->Zname(0); + CMsg_p task_info(*msg); // a deploy message has a special message type + string TaskName = msg->Zname(0); // first static string is the name map::iterator T; - if ((T=TaskMap.find(TaskName)) == TaskMap.end()) + if ((T=TaskMap.find(TaskName)) == TaskMap.end()) // task already exists? { DebugPrint("Inserting new task %s from NameDist\n", TaskName.c_str()); TaskMap[TaskName] = new TaskInfo_t(TaskName); } - DebugPrint("Task %s will have binaries in directory %s\n",msg->Zname(0).c_str(),msg->Zname(1).c_str()); + DebugPrint("Task %s will have binaries in directory %s\n", + msg->Zname(0).c_str(),msg->Zname(1).c_str()); + // most of the deploy information is the core mappings vector> cores; task_info.Get(cores); DebugPrint("Task %s has %d cores\n",TaskName.c_str(),cores.size()); @@ -461,15 +568,20 @@ unsigned Mothership::ConfigDistribute(PMsg_p* msg, unsigned comm) TaskMap[TaskName]->insertCore(core->first, core->second); DebugPrint("%d cores inserted into TaskInfo structure for %s\n",cores.size(),TaskName.c_str()); unsigned err = AddressBook::SUCCESS; - if ((err = SBase::ConfigDistribute(msg, comm)) != AddressBook::SUCCESS) return err; - DebugPrint("About to set binary directory %s for task %s\n",msg->Zname(1).c_str(),msg->Zname(0).c_str()); - return ConfigDir(msg, comm); // distribute message has the directory also + // populate the nameserver entries + if ((err = SBase::ConfigDistribute(msg, comm)) != AddressBook::SUCCESS) + return err; + DebugPrint("About to set binary directory %s for task %s\n", + msg->Zname(1).c_str(),msg->Zname(0).c_str()); + // set the directory for core binaries + return ConfigDir(msg, comm); } //------------------------------------------------------------------------------ unsigned Mothership::ConfigRecall(PMsg_p* msg, unsigned comm) -// remove (recall) a core info block from the Mothership. +// remove (recall) a core info block from the Mothership. This frees up all +// resources owned by a given task to be reallocatable to another task. { string TaskName = msg->Zname(0); map::iterator T; @@ -526,37 +638,45 @@ unsigned Mothership::ConfigRecall(PMsg_p* msg, unsigned comm) //------------------------------------------------------------------------------ unsigned Mothership::ConfigDir(PMsg_p *msg, unsigned comm) -// set the path where binaries for a given task are to be found +// set the path where binaries for a given task are to be found. This can be +// issued as a separate command: task /xpath, although usually task /deploy +// sets this up. { - string task = msg->Zname(0); - string dir = msg->Zname(1); - DebugPrint("Name config dir command received: task %s, directory %s\n", task.c_str(), dir.c_str()); + string task = msg->Zname(0); // task name is in the first static field + string dir = msg->Zname(1); // and the binary path is in the second + DebugPrint("Name config dir command received: task %s, directory %s\n", + task.c_str(), dir.c_str()); map::iterator T; - if ((T=TaskMap.find(task)) == TaskMap.end()) + if ((T=TaskMap.find(task)) == TaskMap.end()) // task already exists? { + // if not set it up. DebugPrint("Inserting new task %s from NameTdir\n", task.c_str()); TaskMap[task] = new TaskInfo_t(task); } TaskMap[task]->BinPath = dir; - return SBase::ConfigDir(msg, comm); + return SBase::ConfigDir(msg, comm); // inform SBase about the path too. } //------------------------------------------------------------------------------ -// A Mothership should never receive a state-configuration name message. It -// should only send them, or update the state internally and call the SBase -// method from the internal update. unsigned Mothership::ConfigState(PMsg_p *msg, unsigned comm) { + /* Overloaded function to deal with a command Q::NAME,Q::CFG,Q::STATE. + A Mothership should never receive a state-configuration name message. It + should only send them, or update the state internally and call the SBase + method from the internal update. + */ // get the name to distinguish the type of error string taskName = msg->Zname(0); if (taskName.empty()) Post(710, "ConfigState", int2str(Urank)); // no task name else if (TaskMap.find(taskName) == TaskMap.end()) Post(715, taskName, int2str(Urank)); // task not loaded to this Mothership - else + // find the task and warn the user that they are attempting something illegal + else { - map::const_iterator st = TaskInfo_t::Task_Status.find(TaskMap[taskName]->status); + map::const_iterator st = + TaskInfo_t::Task_Status.find(TaskMap[taskName]->status); if (st == TaskInfo_t::Task_Status.end()) { Post(550, int2str(Urank), msg->Zname(1), "UNKNOWN"); @@ -571,6 +691,14 @@ unsigned Mothership::ConfigState(PMsg_p *msg, unsigned comm) unsigned Mothership::Connect(string svc) { + /* Connect deals with what happens when a new MPI universe successfully + connects. This can happen, e.g. if a group of Motherships sit independent + of the Root and its associated processes (perhaps physically separated as + well), and the rest of the Orchestrator connects with the (already running) + Motherships which have set themselves up in their own universe and + published a service name by which they may be accessed. Essentially, we + do everything in the constructor related to universe setup. + */ unsigned connErr = MPI_SUCCESS; // set up the connection in the base class if ((connErr = SBase::Connect(svc)) != MPI_SUCCESS) return connErr; @@ -616,6 +744,8 @@ unsigned Mothership::Connect(string svc) void Mothership::Dump(FILE * fp, string task) { +// Comprehensive dump. Includes both all the task information and the NameServer +// data. fprintf(fp,"Mothership dump+++++++++++++++++++++++++++++++++++\n"); fprintf(fp,"Event handler table:\n"); unsigned cIdx = 0; @@ -629,7 +759,8 @@ WALKMAP(unsigned,pMeth,(**F),i) fprintf(fp,"Loaded tasks:\n"); WALKMAP(string,TaskInfo_t*,TaskMap,TaskI) { - fprintf(fp,"Task %s in state %s:\n",TaskI->second->TaskName.c_str(),TaskInfo_t::Task_Status.find(TaskI->second->status)->second.c_str()); + fprintf(fp,"Task %s in state %s:\n",TaskI->second->TaskName.c_str(), + TaskInfo_t::Task_Status.find(TaskI->second->status)->second.c_str()); fprintf(fp,"Reading from binary path %s\n",TaskI->second->BinPath.c_str()); fprintf(fp,"Task map dump:\n"); TaskI->second->VirtualBox->Dump(fp); @@ -645,11 +776,11 @@ else if (task == "*") { vector tasks; unsigned err = AddressBook::SUCCESS; - if ((err = ListTask(tasks))) // get all the tasks + if ((err = ListTask(tasks))) // get all the tasks { - Post(710, "Dump", int2str(Urank)); // error: no tasks were found + Post(710, "Dump", int2str(Urank)); // error: no tasks were found if (fp != stdout) fclose(fp); - return; // so we don't need to do anything + return; // so we don't need to do anything } fprintf(fp,"SBase dump of all tasks______________________\n"); WALKVECTOR(string, tasks, task) SBase::Dump(fp, *task); // dump it all out @@ -705,6 +836,16 @@ unsigned Mothership::DumpTask(PMsg_p * msg, unsigned comm) long Mothership::LoadBoard(P_board* board) { + /* This is the unthreaded version of LoadBoard, the function that does + the data transfer of binaries from files to SDRAM. Currently boards are + loaded 1 core at a time because of hardware limitations. LoadBoard has + 2 separate binaries, a data binary and an executable image, loaded into + different regions as specified in the linker maps generated by the + softswitch builder. Each thread has its own private data space in SDRAM; + each core has a private instruction SRAM, although depending upon + configuration 2 cores - an even and odd-numbered pair, may share + instruction memory (and have to have the same image) + */ long coresLoaded = 0; string task; @@ -718,14 +859,23 @@ long Mothership::LoadBoard(P_board* board) if (!task.empty()) // anything to do? { TaskInfo_t* task_map = TaskMap[task]; - P_addr coreAddress; + P_addr coreAddress, mailboxAddress; uint32_t mX, mY, core, thread; // Intermediates for HostLink-side // address components. DebugPrint("Loading task %s\n", task.c_str()); + // go through all mapped mailboxes, WALKPDIGRAPHNODES(AddressComponent, P_mailbox*, unsigned, P_link*, unsigned, P_port*, board->G, MB) { + // some output is useful here to keep the operator aware that + // we're doing something. + MB->second.data->get_hardware_address()-> + populate_a_software_address(&mailboxAddress); + Post(562, long2str(coresLoaded), + uint2str(mailboxAddress.A_box), + uint2str(mailboxAddress.A_board)); + // and each mapped core on a mapped mailbox WALKMAP(AddressComponent, P_core*, board->G.NodeData(MB)->P_corem, C) { @@ -751,7 +901,6 @@ long Mothership::LoadBoard(P_board* board) &thread); DebugPrint("Loading hardware thread 0x%X at x:%d y:%d c:%d\n", Mothership::GetHWAddr(coreAddress), mX, mY, core); - // Load instruction memory, then data memory. loadInstrsOntoCore(code_f.c_str(), mX, mY, core); loadDataViaCore(data_f.c_str(), mX, mY, core); @@ -769,20 +918,21 @@ long Mothership::LoadBoard(P_board* board) unsigned Mothership::OnCfg(PMsg_p *msg, unsigned comm) { + // top-level instruction dispatch for Q::NAME, Q::CFG command subset switch(msg->L(2)) { - case Q::DIST: - return ConfigDistribute(msg, comm); - case Q::TDIR: - return ConfigDir(msg, comm); + case Q::DIST: // DIST and TDIR both set the binary directory; + return ConfigDistribute(msg, comm); // DIST also loads the core map + case Q::TDIR: + return ConfigDir(msg, comm); case Q::BLD: - return SBase::ConfigBuild(msg, comm); + return SBase::ConfigBuild(msg, comm); // BLD rebuilts SBase maps case Q::RECL: return ConfigRecall(msg, comm); case Q::DEL: return SBase::ConfigDelete(msg, comm); - case Q::STATE: - return ConfigState(msg, comm); + case Q::STATE: // STATE on a mothership will be ignored + return ConfigState(msg, comm); // but issue a warning default: Post(700,uint2str(msg->Key()),int2str(Urank)); return 0; @@ -792,26 +942,28 @@ unsigned Mothership::OnCfg(PMsg_p *msg, unsigned comm) //------------------------------------------------------------------------------ unsigned Mothership::OnCmnd(PMsg_p * Z, unsigned cIdx) -// Handler for a task command sent (probably) from the user. +// Handler for a task command sent (probably) from the user. These handle the +// basic load, start, and stop operations. { -// get the task that the command is going to operate on -string task; -Z->Get(0,task); -unsigned key = Z->Key(); -if (key == PMsg_p::KEY(Q::CMND,Q::LOAD )) -return CmLoad(task); -if (key == PMsg_p::KEY(Q::CMND,Q::RUN )) -return CmRun(task); -if (key == PMsg_p::KEY(Q::CMND,Q::STOP )) -return CmStop(task); -else -Post(510,"Control",int2str(Urank)); -return 0; + // get the task that the command is going to operate on + string task; + Z->Get(0,task); + unsigned key = Z->Key(); + if (key == PMsg_p::KEY(Q::CMND,Q::LOAD )) + return CmLoad(task); + if (key == PMsg_p::KEY(Q::CMND,Q::RUN )) + return CmRun(task); + if (key == PMsg_p::KEY(Q::CMND,Q::STOP )) + return CmStop(task); + else + Post(510,"Control",int2str(Urank)); + return 0; } //------------------------------------------------------------------------------ unsigned Mothership::OnDump(PMsg_p *msg, unsigned comm) +// handlers for various forms of dump { if (msg->L(2) == Q::LIST) return DumpSummary(msg, comm); if (msg->L(2) != Q::TASK) @@ -836,16 +988,20 @@ unsigned Mothership::OnDump(PMsg_p *msg, unsigned comm) unsigned Mothership::OnExit(PMsg_p * Z, unsigned cIdx) // This is what happens when a user command to stop happens { -// We are going away. Shut down any active tasks. -WALKMAP(string, TaskInfo_t*, TaskMap, tsk) -{ - if ((tsk->second->status == TaskInfo_t::TASK_BOOT) || (tsk->second->status == TaskInfo_t::TASK_END)) continue; + // We are going away. Shut down any active tasks. + WALKMAP(string, TaskInfo_t*, TaskMap, tsk) + { + if ((tsk->second->status == TaskInfo_t::TASK_BOOT) || + (tsk->second->status == TaskInfo_t::TASK_END)) continue; + // automatically run tasks at the barrier and immediately stop them; + // this is the most expedient way of stopping tasks that have never + // actually been started. if (tsk->second->status == TaskInfo_t::TASK_BARR) CmRun(tsk->first); if (tsk->second->status == TaskInfo_t::TASK_RUN) CmStop(tsk->first); -} -// stop accepting Tinsel messages -if (twig_running) StopTwig(); -return CommonBase::OnExit(Z,cIdx); // exit through CommonBase handler + } + // stop accepting Tinsel messages + if (twig_running) StopTwig(); + return CommonBase::OnExit(Z,cIdx); // exit through CommonBase handler } //------------------------------------------------------------------------------ @@ -853,24 +1009,25 @@ return CommonBase::OnExit(Z,cIdx); // exit through CommonBase handler void Mothership::OnIdle() // idle processing deals with forwarding packets from the managed Tinsel cores. { - // queues may be changing but we can deal with a static snapshot of the actual - // queue because OnIdle will execute periodically. + // queues may be changing but we can deal with a static snapshot of the + // actual queue because OnIdle will execute periodically. WALKMAP(uint32_t,deque*,TwigExtMap,D) { int NameSrvComm = RootCIdx(); // int NameSrvComm = NameSCIdx(); - PMsg_p W(Comms[NameSrvComm]); // return packets to be routed via the NameServer's comm - W.Key(Q::TINS); // it'll be a Tinsel packet + PMsg_p W(Comms[NameSrvComm]); // return packets to be routed via the NameServer's comm + W.Key(Q::TINS); // it'll be a Tinsel packet W.Tgt(pPmap[NameSrvComm]->U.Root); // temporary: dump external packets to root //W.Tgt(pPmap[NameSrvComm]->U.NameServer); // directed to the NameServer (or UserIO, when we have it) W.Src(Urank); // coming from us - /* well, this is awkward: the PMsg_p type has a Put method for vectors of objects, - which is what we want. Our packet should have a vector of P_Msg_t's. But as things - stand, the messages are trapped in a deque (because we want our twig process to - be able to append to the vector of things to send). Which means copying them out - into a vector. Again, this would be fine if we could copy them directly into a - vector in the PMsg_p, but the interface doesn't allow it - it expects to copy - from vector to vector. So we seem to be stuck with this silly bucket brigade + /* well, this is awkward: the PMsg_p type has a Put method for vectors + of objects, which is what we want. Our packet should have a vector of + P_Msg_t's. But as things stand, the messages are trapped in a deque + (because we want our twig process to be able to append to the vector of + things to send). Which means copying them out into a vector. Again, + this would be fine if we could copy them directly into a vector in the + PMsg_p, but the interface doesn't allow it - it expects to copy from + vector to vector. So we seem to be stuck with this odd bucket brigade approach. NOT the most efficient way to move messages. */ vector packet; @@ -879,8 +1036,8 @@ void Mothership::OnIdle() packet.push_back(D->second->front()); D->second->pop_front(); } - W.Put(0,&packet); // stuff the Tinsel messages into the packet - W.Send(); // and away it goes. + W.Put(0,&packet); // stuff the Tinsel messages into the packet + W.Send(); // and away it goes. } } @@ -905,9 +1062,11 @@ PMsg_p W(Comms[cIdx]); W.Key(Q::SUPR); W.Src(Z->Tgt()); int superReturn = 0; -if ((superReturn = (*SupervisorCall)(Z,&W)) > 0) // Execute. Send a reply if one is expected + // Execute. Send a reply if one is expected +if ((superReturn = (*SupervisorCall)(Z,&W)) > 0) { - if (!cIdx && (Z->Tgt() == Urank) && (Z->Src() == Urank)) OnTinsel(&W, 0); // either to Tinsels, + // either to Tinsels, + if (!cIdx && (Z->Tgt() == Urank) && (Z->Src() == Urank)) OnTinsel(&W, 0); else W.Send(Z->Src()); // or to some external or internal process. } if (superReturn < 0) Post(530, int2str(Urank)); @@ -951,7 +1110,8 @@ return 0; //------------------------------------------------------------------------------ unsigned Mothership::OnTinsel(PMsg_p * Z, unsigned cIdx) -// Handler for direct packets to be injected into the network from an external source +// Handler for direct packets to be injected into the network from an external +// source { vector msgs; // messages are packed in Tinsel message format Z->Get(0, msgs); // We assume they're directly placed in the message @@ -969,27 +1129,36 @@ return 0; //------------------------------------------------------------------------------ unsigned Mothership::OnTinselOut(P_Sup_Msg_t * packet) -// Deals with what happens when a Tinsel message is received. Generally we -// repack the message for delivery to the Supervisor handler and deal with -// it there. The Supervisor can do one of 2 things: A) process it itself, -// possibly generating another message; B) immediately export it over MPI to -// the user Executive or other external process. +/* Deals with what happens when a Tinsel message is received. Generally we + repack the message for delivery to the Supervisor handler and deal with + it there. The Supervisor can do one of 2 things: A) process it itself, + possibly generating another message; B) immediately export it over MPI to + the user Executive or other external process. +*/ { -DebugPrint("Processing a command message 0x%x from Tinsel device %d\n", packet->header.command, packet->header.sourceDeviceAddr); +DebugPrint("Processing a command message 0x%x from Tinsel device %d\n", + packet->header.command, packet->header.sourceDeviceAddr); // handle the kill request from a tinsel core, which generally means an assert failed. if ((packet->header.command == P_SUP_MSG_KILL)) return SystKill(); // output messages can simply be posted to the LogServer as an informational message. if ((packet->header.command == P_SUP_MSG_LOG)) { - DebugPrint("Received a handler_log message from device %d\n", packet->header.sourceDeviceAddr, p_sup_msg_size()); - // Just output the string (this will involve some rubbish at the end where arguments would be; - // to be fixed later). Note that uint8_t*'s have to be reinterpret_casted to char*s. - unsigned msg_len = ((packet->header.cmdLenBytes%p_sup_msg_size()) && (packet->header.seq == packet->header.cmdLenBytes/p_sup_msg_size())) ? packet->header.cmdLenBytes%p_sup_msg_size() : p_sup_msg_size()-p_sup_hdr_size(); + DebugPrint("Received a handler_log message from device %d\n", + packet->header.sourceDeviceAddr, p_sup_msg_size()); + // Just output the string (this will involve some rubbish at the end where + // arguments would be; to be fixed later). Note that uint8_t*'s have to be + // reinterpret_casted to char*s. + unsigned msg_len = ((packet->header.cmdLenBytes%p_sup_msg_size()) && + (packet->header.seq == packet->header.cmdLenBytes/p_sup_msg_size())) ? + packet->header.cmdLenBytes%p_sup_msg_size() : p_sup_msg_size()-p_sup_hdr_size(); msg_len -= p_sup_hdr_size(); - Post(601, int2str(packet->header.sourceDeviceAddr), int2str(packet->header.seq), string(reinterpret_cast(packet->data), msg_len)); + Post(601, int2str(packet->header.sourceDeviceAddr), + int2str(packet->header.seq), + string(reinterpret_cast(packet->data), msg_len)); return 0; } -DebugPrint("Message from device %d is a Supervisor call. Redirecting\n", packet->header.sourceDeviceAddr); +DebugPrint("Message from device %d is a Supervisor call. Redirecting\n", + packet->header.sourceDeviceAddr); PMsg_p W(Comms[0]); // Create a new packet on the local comm W.Key(Q::SUPR); // it'll be a Supervisor packet W.Src(Urank); // coming from the us @@ -1135,47 +1304,78 @@ void* Mothership::Twig(void* par) // and tinsel messsages might be time-critical. while (parent->canRecv()) { - DebugPrint("Message received from a Device\n"); - parent->recv(recv_buf); - uint32_t* device = static_cast(p_recv_buf); // get the first word, which will be a device address + DebugPrint("Message received from a Device\n"); + // this is the receive proper + // get the first word, which will be a device address + parent->recv(recv_buf); + uint32_t* device = static_cast(p_recv_buf); if (!(*device & P_SUP_MASK)) // bound for an external? { P_Msg_Hdr_t* m_hdr = static_cast(p_recv_buf); - DebugPrint("Message is bound for external device %d\n", m_hdr->destDeviceAddr); + DebugPrint("Message is bound for external device %d\n", + m_hdr->destDeviceAddr); + // has a message from this particular device been seen before? + // if not set up a new buffer for the device if (parent->TwigExtMap[m_hdr->destDeviceAddr] == 0) parent->TwigExtMap[m_hdr->destDeviceAddr] = new deque; + // if the message is multi-flit, get the rest of it now if (m_hdr->messageLenBytes > szFlit) parent->recvMsg(recv_buf+szFlit, m_hdr->messageLenBytes-szFlit); parent->TwigExtMap[m_hdr->destDeviceAddr]->push_back(*(static_cast(p_recv_buf))); } else + // message to be consumed on this Mothership, either a system + // message or a Supervisor message { P_Sup_Hdr_t* s_hdr = static_cast(p_recv_buf); s_hdr->sourceDeviceAddr ^= P_SUP_MASK; - if (s_hdr->command == P_PKT_MSGTYP_ALIVE) + // debug message/watchdog: announces that this thread is + // still operational. + if (s_hdr->command == P_PKT_MSGTYP_ALIVE) { DebugPrint("Thread %d is still alive\n", s_hdr->sourceDeviceAddr >> P_THREAD_OS); } else { - DebugPrint("Message is a Supervisor request from device %d\n", s_hdr->sourceDeviceAddr); - if (parent->TwigMap[s_hdr->sourceDeviceAddr] == 0) // new device talking? +//------------------------------------------------------------------------------ + // other messages are routed to the supervisor + DebugPrint("Message is a Supervisor request from device %d\n", + s_hdr->sourceDeviceAddr); + // new device talking? + if (parent->TwigMap[s_hdr->sourceDeviceAddr] == 0) { - DebugPrint("New device %d reporting to Supervisor\n", s_hdr->sourceDeviceAddr); + // supervisors have their own internal device buffers + // (pins) for each device + DebugPrint("New device %d reporting to Supervisor\n", + s_hdr->sourceDeviceAddr); parent->TwigMap[s_hdr->sourceDeviceAddr] = new PinBuf_t; } - if ((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] == 0) // inactive pin for the device? + // inactive pin for the device? + if ((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] == 0) { - DebugPrint("New pin %d for device %d reporting to Supervisor\n", s_hdr->destPin, s_hdr->sourceDeviceAddr); - (*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] = new char[MAX_P_SUP_MSG_BYTES](); + DebugPrint("New pin %d for device %d reporting to Supervisor\n", + s_hdr->destPin, s_hdr->sourceDeviceAddr); + (*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] = + new char[MAX_P_SUP_MSG_BYTES](); } - P_Sup_Msg_t* recvdMsg = static_cast(static_cast((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin])); - memcpy(recvdMsg+s_hdr->seq,s_hdr,p_sup_hdr_size()); // stuff header into the persistent buffer - DebugPrint("Expecting message of total length %d\n", s_hdr->cmdLenBytes); - uint32_t len = s_hdr->seq == s_hdr->cmdLenBytes/p_sup_msg_size() ? s_hdr->cmdLenBytes%p_sup_msg_size() : p_sup_msg_size(); // more message to receive? - DebugPrint("Length for sequence number %d: %d\n", s_hdr->seq, len); - if (len > szFlit) parent->recvMsg(((recvdMsg+s_hdr->seq)->data), len-szFlit); // get the whole message + // convert the buffer into a supervisor message + P_Sup_Msg_t* recvdMsg = + static_cast(static_cast + ((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin])); + // stuff header into the persistent buffer + memcpy(recvdMsg+s_hdr->seq,s_hdr,p_sup_hdr_size()); + DebugPrint("Expecting message of total length %d\n", + s_hdr->cmdLenBytes); + // more message to receive? + uint32_t len = s_hdr->seq == s_hdr->cmdLenBytes/p_sup_msg_size() ? + s_hdr->cmdLenBytes%p_sup_msg_size() : + p_sup_msg_size(); + DebugPrint("Length for sequence number %d: %d\n", + s_hdr->seq, len); + // get the whole message + if (len > szFlit) parent->recvMsg(((recvdMsg+s_hdr->seq)->data), + len-szFlit); if (super_buf_recvd(recvdMsg)) { DebugPrint("Entire Supervisor message received of length %d\n", s_hdr->cmdLenBytes); @@ -1186,7 +1386,8 @@ void* Mothership::Twig(void* par) } } } - // Capture anything happening on the DebugLink - which is text output directed at a file. + // Capture anything happening on the DebugLink - which is text output + // directed at a file. bool updated = false; if (OutFile) { @@ -1198,17 +1399,20 @@ void* Mothership::Twig(void* par) } fgetpos(OutFile, &writePos); } - else while (parent->pollStdOut()); // or possibly only dumped to the local console - // output the debug output buffer, which has to be done immediately because we are in a separate thread + // or possibly only dumped to the local console + else while (parent->pollStdOut()); + // output the debug output buffer, which has to be done immediately + // because we are in a separate thread if (OutFile && updated) { fflush(OutFile); fsetpos(OutFile, &readPos); - while (!feof(OutFile)) parent->Post(600, string(fgets(Line, 4*P_MSG_MAX_SIZE, OutFile))); + while (!feof(OutFile)) + parent->Post(600, string(fgets(Line, 4*P_MSG_MAX_SIZE, OutFile))); fgetpos(OutFile, &readPos); } } - printf("Exiting Twig thread\n"); + DebugPrint("Exiting Twig thread\n"); pthread_exit(par); return par; } diff --git a/Source/Mothership/Mothership.h b/Source/Mothership/Mothership.h index dda195dd..03753634 100644 --- a/Source/Mothership/Mothership.h +++ b/Source/Mothership/Mothership.h @@ -13,7 +13,40 @@ #include "P_addr.h" //============================================================================== - +/* A Mothership is a process resident on a Box - a PC directly connected to the + tinsel network using HostLink. Motherships combine several pieces of + functionality, deriving from 2 classes. + From SBase, Motherships handle local name services and receive a subset of + the name server data related to the devices mapped to hardware resources + directly managed by this Mothership. SBase itself inherits from CommonBase + to implement all the Orchestrator MPI infrastructure. SBase and local + overloads of SBase functions also handle the deploying of tasks to + Motherships. A Deploy command sets up a TaskMap entry - a TaskInfo_t object + which contains the pertinent information related to the state, hardware + resources, and mappings of this task. + From HostLink, Motherships support communications between Tinsels and + the rest of the MPI universe, along with the low-level configuration and + command routines. The inheritance relation emphasises that a Mothership *is* + a HostLink: without this interface it is meaningless, whilst a Mothership + on a given Box is by definition the one and only HostLink on the Box. + A separate thread: Twig, extends the HostLink functionality onto the MPI + network, providing communications *from* tinsels *to* MPI and also via a + private channel to Supervisors. A Supervisor is a bit of application-specific + functionality provided in the XML definition and loaded as a dynamic load + library, for operations like data exfiltration, device configuration and + control, etc. In the absence of an application-defined Supervisor, each + application uses the 'Default Supervisor' which blindly routes packets from + Tinsels to the UserIO external interface process. Non-default Supervisors + inherit all the default Supervisor functionality, so external routing + remains unaffected and is handled through Twig. + A final group of commands and functions not inherited from elsewhere are the + Q::CMND family (through OnCmnd) which handle basic control operations for + tasks: load, start, stop; and the Q::SYST family (through OnSyst) which + handle less-common, operator-level commands and queries aimed at providing + a remote management interface for Motherships themselves (OnSyst may be + expanded in future). + */ +//------------------------------------------------------------------------------ class Mothership : public SBase, public HostLink { diff --git a/Source/Mothership/MothershipMain.cpp b/Source/Mothership/MothershipMain.cpp index e86c4926..6a3b078b 100644 --- a/Source/Mothership/MothershipMain.cpp +++ b/Source/Mothership/MothershipMain.cpp @@ -6,6 +6,8 @@ int main(int argc, char * argv[]) { Mothership* mothership = new Mothership(argc, argv, string(csMOTHERSHIPproc)); + printf("Deleting mothership"); + fflush(stdout); delete mothership; printf("%s Main closing down\n", csMOTHERSHIPproc); fflush(stdout); diff --git a/Source/NameServer/NameServer.cpp b/Source/NameServer/NameServer.cpp index 50b77c60..e9c7b7d3 100644 --- a/Source/NameServer/NameServer.cpp +++ b/Source/NameServer/NameServer.cpp @@ -28,7 +28,7 @@ NameServer::NameServer(int argc,char * argv[],string d): MPISpinner(); // Spin on MPI messages; exit only on DIE -//printf("********* NameServer rank %d on the way out\n",Urank); fflush(stdout); + printf("********* NameServer rank %d on the way out\n",Urank); fflush(stdout); } //------------------------------------------------------------------------------ @@ -37,7 +37,7 @@ NameServer::~NameServer() { WALKVECTOR(FnMap_t*,FnMapx,F) // WALKVECTOR and WALKMAP are in macros.h (long include chain) delete *F; // get rid of derived class function tables -//printf("********* NameServer rank %d destructor\n",Urank); fflush(stdout); + printf("********* NameServer rank %d destructor\n",Urank); fflush(stdout); } //------------------------------------------------------------------------------ diff --git a/Source/OrchBase/HardwareConfigurationDeployment/README.md b/Source/OrchBase/HardwareConfigurationDeployment/README.md index 46fe4203..a16556c3 100644 --- a/Source/OrchBase/HardwareConfigurationDeployment/README.md +++ b/Source/OrchBase/HardwareConfigurationDeployment/README.md @@ -1,4 +1,10 @@ Logic in this directory defines the intermediate translation between hardware -files and configuration datastructure. These intermediate translations -(deployers), can then define PoetsEngines, and their contained items. This -directory also contains logic that defines default configurations. +files and configuration datastructure when reading dialect 1 inputs. These +intermediate translations (deployers), can then define P_engines, and their +contained items. This directory also contains logic that defines default +configurations. + +Dialect 3 input files do not use a deployer; the reader inputs the information +into the Engine directly. + +Dialect 2 input files are not supported. diff --git a/Source/OrchestratorMessages.txt b/Source/OrchestratorMessages.txt index 2399efd9..348aa1f6 100644 --- a/Source/OrchestratorMessages.txt +++ b/Source/OrchestratorMessages.txt @@ -86,7 +86,7 @@ 515(W) : "Task %s not mapped to this mothership (%s)" 516(E) : "Boot thread could not be created for board %s for task %s" 517(W) : "Task %s could not be stopped: state is %s" -518(E) : "Unable to boot task %s completely: %s cores loaded, %s cores expected - aborting" +518(E) : "Unable to boot task completely: %s cores loaded, %s cores expected - aborting" 520(W) : "Hardware command ||%s|| to mothership %s failed" 520(W) : "Unrecognised Mothership system command %s" 530(E) : "Tinsel packet not received successfully on mothership %s" @@ -95,52 +95,11 @@ 533(E) : "Bad symbol %s in the Supervisor library on Mothership %s: check XML definition. Error was %s" 534(E) : "Supervisor library could not be successfully unloaded on Mothership %s. Future attempts to run tasks may have strange I/O behaviour" 540(I) : "Application Supervisor being started on Mothership %s" -550(E) : "Unexpected name services state configuration received on Mothership %s. Asked for state %s, actual state is %s" -560(I) : "Task %s booting..." -561(I) : "Task %s ready to be run" 600(I) : "%s" 601(I) : "Core %s: (part %s): %s" 607(W) : "Attempted to recall a task %s that was not deployed to the Mothership" -700(W) : "Unrecognised command %s to SBase process rank %s" -701(U) : "Bad command switch in SBase process rank %s" -702(W) : "Unrecognised query subtype %s to SBase process rank %s" -703(E) : "Unexpected reply to query: %s received at SBase process rank %s. Circular routing loop?" -704(E) : "Tried to load task into SBase at rank %s with no name" -705(E) : "Error while loading SBase data at rank %s: no task name" -706(E) : "Error %sing device type data for task %s in SBase at rank %s: no device type name" -707(E) : "Error loading device %s for task %s into SBase at rank %s" -708(E) : "Error loading device %s for task %s into SBase at rank %s: no such task" -709(W) : "Unable to register supervisor %s on box %s into SBase: not found in the rank list. May need to connect" -710(W) : "Empty task for SBase %s request at rank %s" -711(S) : "Error: no nameserver found" -712(U) : "Fatal error: nameserver on nonexistent comm" -713(I) : "Sending information to nameserver rank %s from root rank %s on devices on thread %s" -714(I) : "Sending information to nameserver rank %s from root rank %s on supervisors for task %s" -715(I) : "SBase rank %s received information from rank %s on devices for task %s" -716(E) : "Mismatch in device name-data info to SBase at rank %s: %s device names, %s %ss received" -717(E) : "Error sending device data to SBase at rank %s for device %s: invalid record type %s" -718(E) : "Error: no supervisor found for device records for task %s on SBase at rank %s" -719(E) : "Error loading device records for task %s on SBase at rank %s: no such device type %s" -720(W) : "Mismatch in number of device counts sent: %s, expected %s for task %s to SBase at rank %s" -721(E) : "Error %sing task %s on SBase at rank %s" -723(W) : "Task binary directory path to SBase at rank %s is empty. Current working directory will be used" -724(E) : "No task %s on SBase at rank %s" -725(E) : "Error attempting to set state for task %s on SBase at rank %s" -726(I) : "Deploying data to Mothership at rank %s: %s cores distributed" -727(W) : "SBase map on rank %s is invalid. Rebuild before performing tasks on this SBase" -728(E) : "Error: SBase at rank %s has a record for Supervisor rank %s, but no such Supervisor exists" -729(E) : "Supervisor %s registered as a device for Supervisor %s, but only one Supervisor can exist" -730(W) : "Invalid command %s to SBase rank %s: missing value %s" -731(E) : "Query made for a nameless or nonexistent device on SBase rank %s" -732(S) : "SBase at rank %s shows a Supervisor %s for device %s, but no associated %s" -733(E) : "Error looking up by message type on SBase at rank %s: no message type given" -734(W) : "Task %s is in an unknown state according to SBase at rank %s" -735(E) : "No device address received for device name %s to give to process at rank %s" -736(E) : "No associated supervisor address located for device %s to give to process at rank %s" -737(E) : "Mismatch in supervisor data received at rank %s from SBase: %s addresses, %s names, %s ranks" - 801(D) : "P_builder::Add(name=%s,file=%s)" 802(D) : "Removing task %s from the Orchestrator" 803(F) : "P_builder:out of memory to create a QCoreApplication object" diff --git a/Source/Parser/pdeviceinstance.cpp b/Source/Parser/pdeviceinstance.cpp index cf881e0d..8df61d7f 100644 --- a/Source/Parser/pdeviceinstance.cpp +++ b/Source/Parser/pdeviceinstance.cpp @@ -5,6 +5,8 @@ PDeviceInstance::PDeviceInstance(bool is_supervisor, const QString &name, PIGraphObject *parent) : PConcreteInstance(name, is_supervisor ? QString("SDevI") : QString("DevI"), is_supervisor ? QVector() : QVector({STATE}), parent), device(NULL), supervisor(NULL), device_type_id(""), device_type(NULL), supervisor_type(NULL) { + valid_elements["S"] = STATE; + if (is_supervisor && parent) { device_type_id = name; diff --git a/Source/Parser/pdeviceinstance.h b/Source/Parser/pdeviceinstance.h index 264f118e..e8c5a9b3 100644 --- a/Source/Parser/pdeviceinstance.h +++ b/Source/Parser/pdeviceinstance.h @@ -31,7 +31,7 @@ class PDeviceInstance : public PConcreteInstance // 'officially' state is not supported but this seems unlikely. enum vld_elem_types {OTHER, STATE}; - const QHash valid_elements = {{"S", STATE}}; + QHash valid_elements; }; #endif // PDEVICEINSTANCE_H diff --git a/Source/Parser/pdevicetype.cpp b/Source/Parser/pdevicetype.cpp index 15817da0..e39ad2f9 100644 --- a/Source/Parser/pdevicetype.cpp +++ b/Source/Parser/pdevicetype.cpp @@ -6,7 +6,13 @@ PDeviceType::PDeviceType(const QString& name, PIGraphObject *parent) : PConcreteDef(name, "DeviceType", QVector({INPIN, OUTPIN, STATE, CODE}), parent), device_type(NULL) { - + valid_elements["InputPin"] = INPIN; + valid_elements["OutputPin"] = OUTPIN; + valid_elements["State"] = STATE; + valid_elements["SharedCode"] = CODE; + valid_elements["DeviceSharedCode"] = CODE; + valid_elements["ReadyToSend"] = CODE; + valid_elements["OnCompute"] = CODE; } const PIGraphObject* PDeviceType::appendSubObject(QXmlStreamReader* xml_def) diff --git a/Source/Parser/pdevicetype.h b/Source/Parser/pdevicetype.h index 650368df..090ef584 100644 --- a/Source/Parser/pdevicetype.h +++ b/Source/Parser/pdevicetype.h @@ -19,7 +19,7 @@ class PDeviceType : public PConcreteDef private: P_devtyp* device_type; - const QHash valid_elements = {{"InputPin", INPIN}, {"OutputPin", OUTPIN}, {"State", STATE}, {"SharedCode", CODE}, {"DeviceSharedCode", CODE}, {"ReadyToSend", CODE}, {"OnCompute", CODE}}; + QHash valid_elements; }; #endif // PDEVICETYPE_H diff --git a/Source/Parser/pedgeinstance.cpp b/Source/Parser/pedgeinstance.cpp index 6e4877ef..6023384c 100644 --- a/Source/Parser/pedgeinstance.cpp +++ b/Source/Parser/pedgeinstance.cpp @@ -7,7 +7,7 @@ PEdgeInstance::PEdgeInstance(const QString& name, PIGraphObject *parent) : PConcreteInstance(name, "EdgeI", QVector({STATE}), parent), containing_graph(NULL) { - + valid_elements["S"] = STATE; } void PEdgeInstance::defineObject(QXmlStreamReader* xml_def) @@ -61,7 +61,7 @@ void PEdgeInstance::elaborateEdge(D_graph* graph_rep) { PIGraphInstance* parent_instance = dynamic_cast(parent()); if (parent_instance && (parent_instance->graph_type != NULL)) // no parent instance would be a serious elaboration error - { + { // generate or retrieve all the objects required to insert the node into the graph if (path.dst.device == NULL) parsePath(); // build the path if it couldn't be done earlier P_pin* src_pin = new P_pin(graph_rep, path.src.pin->name().toStdString()); diff --git a/Source/Parser/pedgeinstance.h b/Source/Parser/pedgeinstance.h index 643f511e..9dd75dc4 100644 --- a/Source/Parser/pedgeinstance.h +++ b/Source/Parser/pedgeinstance.h @@ -54,7 +54,7 @@ class PEdgeInstance : public PConcreteInstance D_graph* containing_graph; enum vld_elem_types {OTHER, STATE}; - const QHash valid_elements = {{"S", STATE}}; + QHash valid_elements; }; #endif // PEDGEINSTANCE_H diff --git a/Source/Parser/pidatatype.cpp b/Source/Parser/pidatatype.cpp index 3d8fb4db..1292efd7 100644 --- a/Source/Parser/pidatatype.cpp +++ b/Source/Parser/pidatatype.cpp @@ -3,6 +3,14 @@ PIDataType::PIDataType(const QString& name, PIGraphObject *parent) : PIGraphBranch(name, "DataType", QVector({DTYPE}), parent), data_type(NULL), base_type(""), expanded_type(""), value(""), default_value(""), _documentation(""), num_replications(0) { + valid_elements["Scalar"] = SCALAR; + valid_elements["Array"] = ARRAY; + valid_elements["Tuple"] = STRUCT; + valid_elements["Union"] = UNION; + valid_elements["State"] = DTYPE; + valid_elements["Properties"] = DTYPE; + valid_elements["Message"] = DTYPE; + // we will do something more sensible with this in future when a PoetsDataType is better defined. data_type = new PoetsDataType(dynamic_cast(this)); } @@ -231,4 +239,3 @@ const QString& PIDataType::defaultValue() const return default_value; } } - diff --git a/Source/Parser/pidatatype.h b/Source/Parser/pidatatype.h index 829fc3fc..43fce85e 100644 --- a/Source/Parser/pidatatype.h +++ b/Source/Parser/pidatatype.h @@ -47,7 +47,7 @@ class PIDataType : public PIGraphBranch QString _documentation; int num_replications; - const QHash valid_elements = {{"Scalar", SCALAR}, {"Array", ARRAY}, {"Tuple", STRUCT}, {"Union", UNION}, {"State", DTYPE}, {"Properties", DTYPE}, {"Message", DTYPE}}; + QHash valid_elements; }; #endif // PIDATATYPE_H diff --git a/Source/Parser/pigraphinstance.cpp b/Source/Parser/pigraphinstance.cpp index 058cd619..a9bb78cb 100644 --- a/Source/Parser/pigraphinstance.cpp +++ b/Source/Parser/pigraphinstance.cpp @@ -5,7 +5,8 @@ PIGraphInstance::PIGraphInstance(const QString& name, PIGraphObject *parent) : PConcreteInstance(name, "GraphInstance", QVector({DEVINSTS, EDGEINSTS, SUPERINSTS}), parent), graph_type(NULL), supervisor(NULL), graph_instance(NULL), graph_type_id(""), supervisor_type_id("") { - + valid_elements["DeviceInstances"] = DEVINSTS; + valid_elements["EdgeInstances"] = EDGEINSTS; } void PIGraphInstance::defineObject(QXmlStreamReader* xml_def) @@ -161,4 +162,3 @@ P_task* PIGraphInstance::elaborateGraphInstance(OrchBase* orch_root) } return graph_instance; } - diff --git a/Source/Parser/pigraphinstance.h b/Source/Parser/pigraphinstance.h index d96cc0eb..29f1fe42 100644 --- a/Source/Parser/pigraphinstance.h +++ b/Source/Parser/pigraphinstance.h @@ -31,7 +31,7 @@ class PIGraphInstance : public PConcreteInstance QString graph_type_id; QString supervisor_type_id; - const QHash valid_elements = {{"DeviceInstances", DEVINSTS}, {"EdgeInstances", EDGEINSTS}}; + QHash valid_elements; }; diff --git a/Source/Parser/pigraphroot.cpp b/Source/Parser/pigraphroot.cpp index 9baabaf0..3d393296 100644 --- a/Source/Parser/pigraphroot.cpp +++ b/Source/Parser/pigraphroot.cpp @@ -6,7 +6,12 @@ PIGraphRoot::PIGraphRoot(const QString& name, QObject *parent) : PIGraphBranch(name, "Graphs", QVector({HEADER, GTYPE, GINST, METAP}), parent), orchestrator(NULL), obj_id_counter(0), xml_hdr("") { - + valid_elements["Pheader"] = HEADER; + valid_elements["GraphType"] = GTYPE; + valid_elements["GraphTypeReference"] = GTREF; + valid_elements["GraphInstance"] = GINST; + valid_elements["GraphInstanceReference"] = GIREF; + valid_elements["GraphInstanceMetadataPatch"] = METAP; } void PIGraphRoot::defineObject(QXmlStreamReader* xml_def) diff --git a/Source/Parser/pigraphroot.h b/Source/Parser/pigraphroot.h index 1eea7e99..45475b13 100644 --- a/Source/Parser/pigraphroot.h +++ b/Source/Parser/pigraphroot.h @@ -20,7 +20,7 @@ class PIGraphRoot : public PIGraphBranch OrchBase* orchestrator; int obj_id_counter; QString xml_hdr; - const QHash valid_elements = {{"Pheader", HEADER}, {"GraphType", GTYPE}, {"GraphTypeReference", GTREF}, {"GraphInstance", GINST}, {"GraphInstanceReference", GIREF}, {"GraphInstanceMetadataPatch", METAP}}; + QHash valid_elements; }; #endif // PIGRAPHROOT_H diff --git a/Source/Parser/pigraphtype.cpp b/Source/Parser/pigraphtype.cpp index 9710366e..354da82d 100644 --- a/Source/Parser/pigraphtype.cpp +++ b/Source/Parser/pigraphtype.cpp @@ -7,7 +7,9 @@ PIGraphType::PIGraphType(const QString& name, PIGraphObject *parent) : PConcreteDef(name, "GraphType", QVector({DEVTYPES, SUPERDEVTYPES, MSGTYPES, SHAREDCODE}), parent), graph_type(NULL) { - + valid_elements["DeviceTypes"] = DEVTYPES; + valid_elements["MessageTypes"] = MSGTYPES; + valid_elements["SharedCode"] = SHAREDCODE; } const PIGraphObject* PIGraphType::appendSubObject(QXmlStreamReader* xml_def) diff --git a/Source/Parser/pigraphtype.h b/Source/Parser/pigraphtype.h index a024d603..4a72d5fa 100644 --- a/Source/Parser/pigraphtype.h +++ b/Source/Parser/pigraphtype.h @@ -18,7 +18,7 @@ class PIGraphType : public PConcreteDef private: P_typdcl* graph_type; // need to store the graph type here for GraphInstances to recover. - const QHash valid_elements = {{"DeviceTypes", DEVTYPES}, {"MessageTypes", MSGTYPES}, {"SharedCode", SHAREDCODE}}; + QHash valid_elements; }; #endif // PIGRAPHDEF_H diff --git a/Source/Parser/piinputpin.cpp b/Source/Parser/piinputpin.cpp index 7bffa5d3..5400955f 100644 --- a/Source/Parser/piinputpin.cpp +++ b/Source/Parser/piinputpin.cpp @@ -3,7 +3,8 @@ PIInputPin::PIInputPin(const QString& name, PIGraphObject *parent) : PConcreteDef(name, "InputPin", QVector({ONRECEIVE, STATE}), parent), PIPin(parent) { - + valid_elements["OnReceive"] = ONRECEIVE; + valid_elements["State"] = STATE; } void PIInputPin::defineObject(QXmlStreamReader* xml_def) diff --git a/Source/Parser/piinputpin.h b/Source/Parser/piinputpin.h index 0595abba..44e9d41a 100644 --- a/Source/Parser/piinputpin.h +++ b/Source/Parser/piinputpin.h @@ -17,7 +17,7 @@ class PIInputPin : public PConcreteDef, public PIPin enum vld_elem_types {OTHER, ONRECEIVE, STATE}; private: - const QHash valid_elements = {{"OnReceive", ONRECEIVE}, {"State", STATE}}; + QHash valid_elements; }; #endif // PIINPUTPIN_H diff --git a/Source/Parser/pioutputpin.cpp b/Source/Parser/pioutputpin.cpp index 5030f1a1..020228e7 100644 --- a/Source/Parser/pioutputpin.cpp +++ b/Source/Parser/pioutputpin.cpp @@ -2,7 +2,7 @@ PIOutputPin::PIOutputPin(const QString& name, PIGraphObject *parent) : PAnnotatedDef(name, "OutputPin", QVector({ONSEND}), parent), PIPin(parent) { - + valid_elements["OnSend"] = ONSEND; } void PIOutputPin::defineObject(QXmlStreamReader* xml_def) diff --git a/Source/Parser/pioutputpin.h b/Source/Parser/pioutputpin.h index 4fdf75f8..bac44139 100644 --- a/Source/Parser/pioutputpin.h +++ b/Source/Parser/pioutputpin.h @@ -18,7 +18,7 @@ class PIOutputPin : public PAnnotatedDef, public PIPin private: enum vld_elem_types {OTHER, ONSEND}; - const QHash valid_elements = {{"OnSend", ONSEND}}; + QHash valid_elements; }; #endif // PIOUTPUTPIN_H diff --git a/Source/Parser/pmessagetype.cpp b/Source/Parser/pmessagetype.cpp index 1cf6d48f..f979e395 100644 --- a/Source/Parser/pmessagetype.cpp +++ b/Source/Parser/pmessagetype.cpp @@ -3,7 +3,7 @@ PMessageType::PMessageType(const QString& name, PIGraphObject *parent) : PAnnotatedDef(name, "MessageType", QVector({MSG}), parent), message_type(NULL) { - + valid_elements["Message"] = MSG; } const PIGraphObject* PMessageType::appendSubObject(QXmlStreamReader* xml_def) diff --git a/Source/Parser/pmessagetype.h b/Source/Parser/pmessagetype.h index 60a20b24..7d989f6e 100644 --- a/Source/Parser/pmessagetype.h +++ b/Source/Parser/pmessagetype.h @@ -16,7 +16,7 @@ class PMessageType : public PAnnotatedDef private: P_message* message_type; enum vld_elem_types {OTHER, MSG}; - const QHash valid_elements = {{"Message", MSG}}; + QHash valid_elements; }; #endif // PMESSAGETYPE_H diff --git a/Source/Parser/poetsdatatype.cpp b/Source/Parser/poetsdatatype.cpp index b7702f3b..e29e3725 100644 --- a/Source/Parser/poetsdatatype.cpp +++ b/Source/Parser/poetsdatatype.cpp @@ -2,5 +2,52 @@ PoetsDataType::PoetsDataType(QObject* parent) : QObject(parent), QVariant() { + vld_types["char"] = ISCALAR; + vld_types["short"] = ISCALAR; + vld_types["int"] = ISCALAR; + vld_types["long"] = ISCALAR; + vld_types["long long"] = ISCALAR; + vld_types["unsigned char"] = USCALAR; + vld_types["unsigned short"] = USCALAR; + vld_types["unsigned int"] = USCALAR; + vld_types["unsigned long"] = USCALAR; + vld_types["unsigned long long"] = USCALAR; + vld_types["int8_t"] = ISCALAR; + vld_types["int16_t"] = ISCALAR; + vld_types["int32_t"] = ISCALAR; + vld_types["int64_t"] = ISCALAR; + vld_types["uint8_t"] = USCALAR; + vld_types["uint16_t"] = USCALAR; + vld_types["uint32_t"] = USCALAR; + vld_types["uint64_t"] = USCALAR; + vld_types["float"] = FSCALAR; + vld_types["double"] = FSCALAR; + vld_types["long double"] = FSCALAR; + vld_types["char*"] = SSCALAR; + vld_types["string"] = SSCALAR; + vld_types["union"] = UNION; + vld_types["struct"] = STRUCT; + type_sizes["char"] = sizeof(char); + type_sizes["short"] = sizeof(short); + type_sizes["int"] = sizeof(int); + type_sizes["long"] = sizeof(long); + type_sizes["long long"] = sizeof(long long); + type_sizes["unsigned char"] = sizeof(unsigned char); + type_sizes["unsigned short"] = sizeof(unsigned short); + type_sizes["unsigned int"] = sizeof(unsigned int); + type_sizes["unsigned long"] = sizeof(unsigned long); + type_sizes["unsigned long long"] = sizeof(unsigned long long); + type_sizes["int8_t"] = sizeof(int8_t); + type_sizes["int16_t"] = sizeof(int16_t); + type_sizes["int32_t"] = sizeof(int32_t); + type_sizes["int64_t"] = sizeof(int64_t); + type_sizes["uint8_t"] = sizeof(uint8_t); + type_sizes["uint16_t"] = sizeof(uint16_t); + type_sizes["uint32_t"] = sizeof(uint32_t); + type_sizes["uint64_t"] = sizeof(uint64_t); + type_sizes["float"] = sizeof(float); + type_sizes["double"] = sizeof(double); + type_sizes["long double"] = sizeof(long double); + type_sizes["char*"] = sizeof(char*); } diff --git a/Source/Parser/poetsdatatype.h b/Source/Parser/poetsdatatype.h index 88d5a2a4..46d8d4a4 100644 --- a/Source/Parser/poetsdatatype.h +++ b/Source/Parser/poetsdatatype.h @@ -12,14 +12,10 @@ class PoetsDataType : public QObject, public QVariant enum dtype_classes {ISCALAR, USCALAR, FSCALAR, SSCALAR, ARRAY, STRUCT, UNION}; - const QHash vld_types = {{"char", ISCALAR}, {"short", ISCALAR}, {"int", ISCALAR}, {"long", ISCALAR}, {"long long", ISCALAR}, {"unsigned char", USCALAR}, {"unsigned short", USCALAR}, {"unsigned int", USCALAR}, {"unsigned long", USCALAR}, {"unsigned long long", USCALAR}, {"int8_t", ISCALAR}, - {"int16_t", ISCALAR}, {"int32_t", ISCALAR}, {"int64_t", ISCALAR}, {"uint8_t", USCALAR}, {"uint16_t", USCALAR}, {"uint32_t", USCALAR}, {"uint64_t", USCALAR}, - {"float", FSCALAR}, {"double", FSCALAR}, {"long double", FSCALAR}, {"char*", SSCALAR}, {"string", SSCALAR}, {"union", UNION}, {"struct", STRUCT}}; - const QHash type_sizes = {{"char", sizeof(char)}, {"short", sizeof(short)}, {"int", sizeof(int)}, {"long", sizeof(long)}, {"long long", sizeof(long long)}, {"unsigned char", sizeof(unsigned char)}, {"unsigned short", sizeof(unsigned short)}, {"unsigned int", sizeof(unsigned int)}, - {"unsigned long", sizeof(unsigned long)}, {"unsigned long long", sizeof(unsigned long long)}, {"int8_t", sizeof(int8_t)}, {"int16_t", sizeof(int16_t)}, {"int32_t", sizeof(int32_t)}, {"int64_t", sizeof(int64_t)}, {"uint8_t", sizeof(uint8_t)}, - {"uint16_t", sizeof(uint16_t)}, {"uint32_t", sizeof(uint32_t)}, {"uint64_t", sizeof(uint64_t)}, {"float", sizeof(float)}, {"double", sizeof(double)}, {"long double", sizeof(long double)}, {"char*", sizeof(char*)}}; + QHash vld_types; + QHash type_sizes; -/* const QHash*> vld_typ_classes = {{ISCALAR, &int_scalars}, {FSCALAR, &float_scalars}, {SSCALAR, &string_scalars}, {STRUCT, &_unions}, {UNION, &_structs}}; +/* QHash*> vld_typ_classes = {{ISCALAR, &int_scalars}, {FSCALAR, &float_scalars}, {SSCALAR, &string_scalars}, {STRUCT, &_unions}, {UNION, &_structs}}; private: const QVector int_scalars = {"char", "short", "int", "long", "long long", "unsigned char", "unsigned short", "unsigned int", "unsigned long", "unsigned long long", "int8_t", "int16_t", "int32_t", "int64_t", "uint8_t", "uint16_t", "uint32_t", "uint64_t"}; diff --git a/Source/Parser/psupervisordevicetype.cpp b/Source/Parser/psupervisordevicetype.cpp index f5c88415..3fb0985f 100644 --- a/Source/Parser/psupervisordevicetype.cpp +++ b/Source/Parser/psupervisordevicetype.cpp @@ -7,7 +7,9 @@ PSupervisorDeviceType::PSupervisorDeviceType(const QString& name, PIGraphObject PIGraphBranch(name, "SupervisorDeviceType", QVector({INPIN, OUTPIN, CODE}), parent), dev_info_persistent(false), dev_props_valid(false), edge_endpts_exist(false), device_type(0) { - + valid_elements["InputPin"] = INPIN; + valid_elements["OutputPin"] = OUTPIN; + valid_elements["Code"] = CODE; } void PSupervisorDeviceType::defineObject(QXmlStreamReader* xml_def) diff --git a/Source/Parser/psupervisordevicetype.h b/Source/Parser/psupervisordevicetype.h index bca38c4d..257e2e5f 100644 --- a/Source/Parser/psupervisordevicetype.h +++ b/Source/Parser/psupervisordevicetype.h @@ -22,7 +22,7 @@ class PSupervisorDeviceType : public PIGraphBranch private: P_devtyp* device_type; - const QHash valid_elements = {{"InputPin", INPIN}, {"OutputPin", OUTPIN}, {"Code", CODE}}; + QHash valid_elements; }; #endif // PSUPERVISORDEVICETYPE_H diff --git a/Source/Softswitch/Makefile b/Source/Softswitch/Makefile index 48886fed..02ccf3d0 100644 --- a/Source/Softswitch/Makefile +++ b/Source/Softswitch/Makefile @@ -39,7 +39,7 @@ endif ifdef TRIVIAL_LOG_HANDLER SOFTSWITCH_DEPS = handler_log_t.h -LH_CFLAGS = -D TRIVIAL_LOG_HANDLER +LH_CFLAGS = -D TRIVIAL_LOG_HANDLER endif # MPI tools @@ -100,7 +100,7 @@ SECEXPOS := %.o TARGETS := $(patsubst $(GENINC)/vars_%.h,$(BIN_DIR)/softswitch_%.elf,$(wildcard $(GENINC)/vars_*.h)) VCTARGETS := $(patsubst $(GENINC)/vars_%.h,$(BIN_DIR)/softswitch_code_%.v,$(wildcard $(GENINC)/vars_*.h)) VDTARGETS := $(patsubst $(GENINC)/vars_%.h,$(BIN_DIR)/softswitch_data_%.v,$(wildcard $(GENINC)/vars_*.h)) -# targets for thread-local variables +# targets for thread-local variables THREADVARS := $(patsubst $(GENSRC)/%.cpp,%.o,$(wildcard $(GENSRC)/vars_*_*.cpp)) .PHONY: all clean printvars supervisor @@ -130,7 +130,7 @@ softswitch.o : softswitch.cpp softswitch.h poets_msg.h poets_hardware.h tinsel.h poets_msg.o : poets_msg.cpp poets_msg.h poets_hardware.h $(TINSELINC)/config.h $(RV_CPPC) $(CFLAGS) -Wall -c -o $@ $< -io.o : io.c io.h tinsel.h $(TINSELINC)/config.h +io.o : io.c io.h tinsel.h $(TINSELINC)/config.h $(RV_CPPC) $(CFLAGS) -Wall -c -o $@ $< ## the pattern substitution should find all vars_x_y.o created from vars_x_y.cpp diff --git a/Source/Softswitch/inc/poets_msg.h b/Source/Softswitch/inc/poets_msg.h index 26fdf6a1..c140f161 100644 --- a/Source/Softswitch/inc/poets_msg.h +++ b/Source/Softswitch/inc/poets_msg.h @@ -22,6 +22,10 @@ #define P_BOX_OS (LOG_BOARDS_PER_BOX+P_BOARD_OS) #define P_SUP_OS 31 #define P_SUP_MASK (0x1 << P_SUP_OS) + +//------------------------------------------------------------------------------ +// TODO: rationalise these defines as they are superfluous? +//------------------------------------------------------------------------------ #define P_BOX_MASK ((0x1 << P_SUP_OS) - (0x1 << P_BOX_OS)) #define P_BOARD_MASK ((0x1 << P_BOX_OS) - (0x1 << P_BOARD_OS)) #define P_CORE_MASK ((0x1 << P_BOARD_OS) - (0x1 << P_CORE_OS)) @@ -36,6 +40,8 @@ #define P_BOARD_HWMASK ((0x1 << P_BOX_HWOS) - (0x1 << P_BOARD_HWOS)) #define P_CORE_HWMASK ((0x1 << P_BOARD_HWOS) - (0x1 << P_CORE_HWOS)) #define P_THREAD_HWMASK ((0x1 << P_CORE_HWOS) - (0x1 << P_THREAD_HWOS)) +//------------------------------------------------------------------------------ + #define P_PKT_MSGTYP_OS 10 #define P_PKT_MSGTYP_BARRIER 0x1000 #define P_PKT_MSGTYP_SUPER 0x2000 @@ -90,10 +96,10 @@ typedef struct p_super_message const unsigned int p_msg_pyld_size = sizeof(P_Msg_t)-sizeof(P_Msg_Hdr_t); const unsigned int p_super_data_size = sizeof(P_Sup_Msg_t)-sizeof(P_Sup_Hdr_t); -inline size_t p_msg_size() {return sizeof(P_Msg_t);}; -inline size_t p_hdr_size() {return sizeof(P_Msg_Hdr_t);}; -inline size_t p_sup_msg_size() {return sizeof(P_Sup_Msg_t);}; -inline size_t p_sup_hdr_size() {return sizeof(P_Sup_Hdr_t);}; +inline size_t p_msg_size() {return sizeof(P_Msg_t);} +inline size_t p_hdr_size() {return sizeof(P_Msg_Hdr_t);} +inline size_t p_sup_msg_size() {return sizeof(P_Sup_Msg_t);} +inline size_t p_sup_hdr_size() {return sizeof(P_Sup_Hdr_t);} // message buffers (last argument) in the message setters must be volatile because we might wish // to write directly to a hardware resource containing the buffer, which in general may be volatile. void set_msg_hdr(uint32_t, uint32_t, uint8_t, uint8_t, uint16_t = 0, P_Msg_Hdr_t* = 0); diff --git a/Tests/StaticResources/aesop_dialect_1.uif b/Tests/StaticResources/Dialect1/aesop_dialect_1.uif similarity index 100% rename from Tests/StaticResources/aesop_dialect_1.uif rename to Tests/StaticResources/Dialect1/aesop_dialect_1.uif diff --git a/Tests/StaticResources/duplicate_section_invalid.uif b/Tests/StaticResources/Dialect1/duplicate_section_invalid.uif similarity index 100% rename from Tests/StaticResources/duplicate_section_invalid.uif rename to Tests/StaticResources/Dialect1/duplicate_section_invalid.uif diff --git a/Tests/StaticResources/invalid_section.uif b/Tests/StaticResources/Dialect1/invalid_section.uif similarity index 100% rename from Tests/StaticResources/invalid_section.uif rename to Tests/StaticResources/Dialect1/invalid_section.uif diff --git a/Tests/StaticResources/missing_section_invalid.uif b/Tests/StaticResources/Dialect1/missing_section_invalid.uif similarity index 100% rename from Tests/StaticResources/missing_section_invalid.uif rename to Tests/StaticResources/Dialect1/missing_section_invalid.uif diff --git a/Tests/StaticResources/syntactically_invalid_test_file.uif b/Tests/StaticResources/Dialect1/syntactically_invalid_test_file.uif similarity index 100% rename from Tests/StaticResources/syntactically_invalid_test_file.uif rename to Tests/StaticResources/Dialect1/syntactically_invalid_test_file.uif diff --git a/Tests/StaticResources/valid_test_file.uif b/Tests/StaticResources/Dialect1/valid_test_file.uif similarity index 100% rename from Tests/StaticResources/valid_test_file.uif rename to Tests/StaticResources/Dialect1/valid_test_file.uif diff --git a/Tests/StaticResources/Dialect3/Invalid/README.txt b/Tests/StaticResources/Dialect3/Invalid/README.txt new file mode 100644 index 00000000..22471e5a --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/README.txt @@ -0,0 +1,4 @@ +This directory contains example UIF files that should fail under the Dialect 3 +reader. + +If you want to add test cases here, be sure to modify the test script. diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_board_name_too_short.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_board_name_too_short.uif new file mode 100644 index 00000000..9ff6883f --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_board_name_too_short.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_board_name_too_short.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- Q -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- Q -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,Q,B11,B20,B21)) +El(addr(1),boards(B00,B01,Q,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(Q)),By(board(B01)) +(1,0):By(board(Q),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(Q)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(Q)),By(board(B01)),By(board(B21)),El(board(Q)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(Q)),El(board(B01)) +(1,2):El(board(Q),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(Q)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(Q)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_box_name_too_long.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_box_name_too_long.uif new file mode 100644 index 00000000..31dc0f7b --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_box_name_too_long.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_box_name_too_long.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +ThirtyThreeCharacterLongXFailName(boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):ThirtyThreeCharacterLongBadBoxName(board(B00),addr(000,000))=ThirtyThreeCharacterLongBadBoxName(board(B10)),ThirtyThreeCharacterLongBadBoxName(board(B01)) +(1,0):ThirtyThreeCharacterLongBadBoxName(board(B10),addr(000,001))=ThirtyThreeCharacterLongBadBoxName(board(B00)),ThirtyThreeCharacterLongBadBoxName(board(B20)),ThirtyThreeCharacterLongBadBoxName(board(B11)) +(2,0):ThirtyThreeCharacterLongBadBoxName(board(B20),addr(000,010))=ThirtyThreeCharacterLongBadBoxName(board(B10)),ThirtyThreeCharacterLongBadBoxName(board(B21)) +(0,1):ThirtyThreeCharacterLongBadBoxName(board(B01),addr(001,000))=ThirtyThreeCharacterLongBadBoxName(board(B00)),ThirtyThreeCharacterLongBadBoxName(board(B11)),El(board(B00)) +(1,1):ThirtyThreeCharacterLongBadBoxName(board(B11),addr(001,001))=ThirtyThreeCharacterLongBadBoxName(board(B10)),ThirtyThreeCharacterLongBadBoxName(board(B01)),ThirtyThreeCharacterLongBadBoxName(board(B21)),El(board(B10)) +(2,1):ThirtyThreeCharacterLongBadBoxName(board(B21),addr(001,010))=ThirtyThreeCharacterLongBadBoxName(board(B20)),ThirtyThreeCharacterLongBadBoxName(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=ThirtyThreeCharacterLongBadBoxName(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=ThirtyThreeCharacterLongBadBoxName(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=ThirtyThreeCharacterLongBadBoxName(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_broken_header_variable_1.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_broken_header_variable_1.uif new file mode 100644 index 00000000..79596aaf --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_broken_header_variable_1.uif @@ -0,0 +1,126 @@ +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version=("0.3.2","4") ++file="invalid_dialect_3_broken_header_variable_1.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_broken_header_variable_2.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_broken_header_variable_2.uif new file mode 100644 index 00000000..85f73fad --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_broken_header_variable_2.uif @@ -0,0 +1,126 @@ +[header(ByronEliot)] ++(author,problem)="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_broken_header_variable_2.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_duplicate_section.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_duplicate_section.uif new file mode 100644 index 00000000..e791bbee --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_duplicate_section.uif @@ -0,0 +1,136 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_duplicate_section.uif" + +[header(SomethingElseBad)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_duplicate_section.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_empty.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_empty.uif new file mode 100644 index 00000000..e69de29b diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_floating_dram.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_floating_dram.uif new file mode 100644 index 00000000..4e2bbfc1 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_floating_dram.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_floating_dram.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096.5 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_invalid_character_in_type.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_invalid_character_in_type.uif new file mode 100644 index 00000000..d5af7d88 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_invalid_character_in_type.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_invalid_character_in_type.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="Common,Mbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(Common,Mbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_board_type.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_board_type.uif new file mode 100644 index 00000000..4861970b --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_board_type.uif @@ -0,0 +1,128 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_board_type.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_box_type.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_box_type.uif new file mode 100644 index 00000000..512998c8 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_box_type.uif @@ -0,0 +1,128 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_box_type.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_cost.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_cost.uif new file mode 100644 index 00000000..472ff7de --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_cost.uif @@ -0,0 +1,128 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_cost.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mailbox_type.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mailbox_type.uif new file mode 100644 index 00000000..66fe8195 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mailbox_type.uif @@ -0,0 +1,128 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_mailbox_type.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mandatory_header_item.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mandatory_header_item.uif new file mode 100644 index 00000000..fc78ccae --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mandatory_header_item.uif @@ -0,0 +1,128 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_mandatory_header_item.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mandatory_header_variable_1.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mandatory_header_variable_1.uif new file mode 100644 index 00000000..f7fac504 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mandatory_header_variable_1.uif @@ -0,0 +1,126 @@ + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++version="0.3.2" ++file="invalid_dialect_3_missing_mandatory_header_variable_1.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mandatory_header_variable_2.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mandatory_header_variable_2.uif new file mode 100644 index 00000000..4432a955 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_mandatory_header_variable_2.uif @@ -0,0 +1,126 @@ + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++file="invalid_dialect_3_missing_mandatory_header_variable_2.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_packet_length.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_packet_length.uif new file mode 100644 index 00000000..ebad9686 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_packet_length.uif @@ -0,0 +1,128 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_packet_length.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_board.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_board.uif new file mode 100644 index 00000000..cb42ff08 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_board.uif @@ -0,0 +1,128 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_referenced_board.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_board_2.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_board_2.uif new file mode 100644 index 00000000..3a8018d2 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_board_2.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_referenced_board_2.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21,Octopus)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21,Locust)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_box.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_box.uif new file mode 100644 index 00000000..16126ad3 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_box.uif @@ -0,0 +1,128 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_referenced_box.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_mailbox.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_mailbox.uif new file mode 100644 index 00000000..69be618c --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_mailbox.uif @@ -0,0 +1,128 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_referenced_mailbox.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_mailbox_2.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_mailbox_2.uif new file mode 100644 index 00000000..50fee29c --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_referenced_mailbox_2.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_referenced_mailbox_2.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,Octopus +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_reverse_edge_board_definition.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_reverse_edge_board_definition.uif new file mode 100644 index 00000000..bc56d782 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_reverse_edge_board_definition.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_reverse_edge_board_definition.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_reverse_edge_mailbox_definition.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_reverse_edge_mailbox_definition.uif new file mode 100644 index 00000000..6e261c7e --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_reverse_edge_mailbox_definition.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_reverse_edge_mailbox_definition.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_section.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_section.uif new file mode 100644 index 00000000..15cb46b4 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_missing_section.uif @@ -0,0 +1,121 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_missing_section.uif" + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_nonfloat_cost.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_nonfloat_cost.uif new file mode 100644 index 00000000..6c163a57 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_nonfloat_cost.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_nonfloat_cost.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=a ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_type_too_long.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_type_too_long.uif new file mode 100644 index 00000000..1f0a7657 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_type_too_long.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_type_too_long.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBoxCommonBoxCommonBoxCommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBoxCommonBoxCommonBoxCommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_type_too_short.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_type_too_short.uif new file mode 100644 index 00000000..e2ab161b --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_type_too_short.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_type_too_short.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="C" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(C)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_undefined_type.uif b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_undefined_type.uif new file mode 100644 index 00000000..066983b9 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Invalid/invalid_dialect_3_undefined_type.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_undefined_type.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="Octopus" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Valid/8_box.uif b/Tests/StaticResources/Dialect3/Valid/8_box.uif new file mode 100644 index 00000000..06ad51b6 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Valid/8_box.uif @@ -0,0 +1,193 @@ +// A representation of the 8-box POETS Engine in Dialect 3. Costs are +// unknown. + +[header("8Box")] ++author="Mark Vousden" ++dialect=3 ++datetime=20190823150800 ++version="0.4.0" ++file="8_box.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=3 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Heaney (He) Ibsen (Ib) +// +-------------------+ +-------------------+ +// | | | | +// | B01 -- B11 -- B21 --- B01 -- B11 -- B21 | +// | | | | +// | | | | | | | | | | +// | | | | +// | B00 -- B10 -- B20 --- B00 -- B10 -- B20 | +// | | | | +// +--|------|------|--+ +--|------|------|--+ +// Fielding (Fe) Goethe (Go) +// +--|------|------|--+ +--|------|------|--+ +// | | | | +// | B01 -- B11 -- B21 --- B01 -- B11 -- B21 | +// | | | | +// | | | | | | | | | | +// | | | | +// | B00 -- B10 -- B20 --- B00 -- B10 -- B20 | +// | | | | +// +--|------|------|--+ +--|------|------|--+ +// Defoe (De) Eliot (El) +// +--|------|------|--+ +--|------|------|--+ +// | | | | +// | B01 -- B11 -- B21 --- B01 -- B11 -- B21 | +// | | | | +// | | | | | | | | | | +// | | | | +// | B00 -- B10 -- B20 --- B00 -- B10 -- B20 | +// | | | | +// +--|------|------|--+ +--|------|------|--+ +// Byron (By) Coleridge (Co) +// +--|------|------|--+ +--|------|------|--+ +// | | | | +// | B01 -- B11 -- B21 --- B01 -- B11 -- B21 | +// | | | | +// | | | | | | | | | | +// | | | | +// | B00 -- B10 -- B20 --- B00 -- B10 -- B20 | +// | | | | +// +-------------------+ +-------------------+ +// +[engine_box] +By(addr(000),boards(B00,B01,B10,B11,B20,B21),hostname(byron)) +Co(addr(001),boards(B00,B01,B10,B11,B20,B21),hostname(coleridge)) +De(addr(010),boards(B00,B01,B10,B11,B20,B21),hostname(defoe)) +El(addr(011),boards(B00,B01,B10,B11,B20,B21),hostname(eliot)) +Fi(addr(100),boards(B00,B01,B10,B11,B20,B21),hostname(fielding)) +Go(addr(101),boards(B00,B01,B10,B11,B20,B21),hostname(goethe)) +He(addr(110),boards(B00,B01,B10,B11,B20,B21),hostname(heaney)) +Ib(addr(111),boards(B00,B01,B10,B11,B20,B21),hostname(ibsen)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)),Co(board(B00)) +(3,0):Co(board(B00),addr(000,011))=By(board(B20)),Co(board(B10)),Co(board(B01)) +(4,0):Co(board(B10),addr(000,100))=Co(board(B00)),Co(board(B20)),Co(board(B11)) +(5,0):Co(board(B20),addr(000,101))=Co(board(B10)),Co(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),De(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),De(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),Co(board(B01)),De(board(B20)) +(3,1):Co(board(B01),addr(001,011))=Co(board(B00)),By(board(B21)),Co(board(B11)),El(board(B00)) +(4,1):Co(board(B11),addr(001,100))=Co(board(B10)),Co(board(B01)),Co(board(B21)),El(board(B10)) +(5,1):Co(board(B21),addr(001,101))=Co(board(B20)),Co(board(B11)),El(board(B20)) +(0,2):De(board(B00),addr(010,000))=By(board(B01)),De(board(B10)),De(board(B01)) +(1,2):De(board(B10),addr(010,001))=By(board(B11)),De(board(B00)),De(board(B20)),De(board(B11)) +(2,2):De(board(B20),addr(010,010))=By(board(B21)),De(board(B10)),De(board(B21)),El(board(B00)) +(3,2):El(board(B00),addr(010,011))=Co(board(B01)),De(board(B20)),El(board(B10)),El(board(B01)) +(4,2):El(board(B10),addr(010,100))=Co(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(5,2):El(board(B20),addr(010,101))=Co(board(B21)),El(board(B10)),El(board(B21)) +(0,3):De(board(B01),addr(011,000))=De(board(B00)),De(board(B11)),Fi(board(B00)) +(1,3):De(board(B11),addr(011,001))=De(board(B10)),De(board(B01)),De(board(B21)),Fi(board(B10)) +(2,3):De(board(B21),addr(011,010))=De(board(B20)),De(board(B11)),El(board(B01)),Fi(board(B20)) +(3,3):El(board(B01),addr(011,011))=El(board(B00)),De(board(B21)),El(board(B11)),Go(board(B00)) +(4,3):El(board(B11),addr(011,100))=El(board(B10)),El(board(B01)),El(board(B21)),Go(board(B10)) +(5,3):El(board(B21),addr(011,101))=El(board(B20)),El(board(B11)),Go(board(B20)) +(0,4):Fi(board(B00),addr(100,000))=De(board(B01)),Fi(board(B10)),Fi(board(B01)) +(1,4):Fi(board(B10),addr(100,001))=De(board(B11)),Fi(board(B00)),Fi(board(B20)),Fi(board(B11)) +(2,4):Fi(board(B20),addr(100,010))=De(board(B21)),Fi(board(B10)),Fi(board(B21)),Go(board(B00)) +(3,4):Go(board(B00),addr(100,011))=El(board(B01)),Fi(board(B20)),Go(board(B10)),Go(board(B01)) +(4,4):Go(board(B10),addr(100,100))=El(board(B11)),Go(board(B00)),Go(board(B20)),Go(board(B11)) +(5,4):Go(board(B20),addr(100,101))=El(board(B21)),Go(board(B10)),Go(board(B21)) +(0,5):Fi(board(B01),addr(101,000))=Fi(board(B00)),Fi(board(B11)),He(board(B00)) +(1,5):Fi(board(B11),addr(101,001))=Fi(board(B10)),Fi(board(B01)),Fi(board(B21)),He(board(B10)) +(2,5):Fi(board(B21),addr(101,010))=Fi(board(B20)),Fi(board(B11)),Go(board(B01)),He(board(B20)) +(3,5):Go(board(B01),addr(101,011))=Go(board(B00)),Fi(board(B21)),Go(board(B11)),Ib(board(B00)) +(4,5):Go(board(B11),addr(101,100))=Go(board(B10)),Go(board(B01)),Go(board(B21)),Ib(board(B10)) +(5,5):Go(board(B21),addr(101,101))=Go(board(B20)),Go(board(B11)),Ib(board(B20)) +(0,6):He(board(B00),addr(110,000))=Fi(board(B01)),He(board(B10)),He(board(B01)) +(1,6):He(board(B10),addr(110,001))=Fi(board(B11)),He(board(B00)),He(board(B20)),He(board(B11)) +(2,6):He(board(B20),addr(110,010))=Fi(board(B21)),He(board(B10)),He(board(B21)),Ib(board(B00)) +(3,6):Ib(board(B00),addr(110,011))=Go(board(B01)),He(board(B20)),Ib(board(B10)),Ib(board(B01)) +(4,6):Ib(board(B10),addr(110,100))=Go(board(B11)),Ib(board(B00)),Ib(board(B20)),Ib(board(B11)) +(5,6):Ib(board(B20),addr(110,101))=Go(board(B21)),Ib(board(B10)),Ib(board(B21)) +(0,7):He(board(B01),addr(111,000))=He(board(B00)),He(board(B11)) +(1,7):He(board(B11),addr(111,001))=He(board(B10)),He(board(B01)),He(board(B21)) +(2,7):He(board(B21),addr(111,010))=He(board(B20)),He(board(B11)),Ib(board(B01)) +(3,7):Ib(board(B01),addr(111,011))=Ib(board(B00)),He(board(B21)),Ib(board(B11)) +(4,7):Ib(board(B11),addr(111,100))=Ib(board(B10)),Ib(board(B01)),Ib(board(B21)) +(5,7):Ib(board(B21),addr(111,101))=Ib(board(B20)),Ib(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Valid/README.txt b/Tests/StaticResources/Dialect3/Valid/README.txt new file mode 100644 index 00000000..9feeba2c --- /dev/null +++ b/Tests/StaticResources/Dialect3/Valid/README.txt @@ -0,0 +1,4 @@ +This directory contains example UIF files that should pass under the Dialect 3 +reader. + +If you want to add test cases here, be sure to modify the test script. diff --git a/Tests/StaticResources/Dialect3/Valid/valid_dialect_3.uif b/Tests/StaticResources/Dialect3/Valid/valid_dialect_3.uif new file mode 100644 index 00000000..ea9394df --- /dev/null +++ b/Tests/StaticResources/Dialect3/Valid/valid_dialect_3.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="valid_dialect_3.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Valid/valid_dialect_3_mismatched_name.uif b/Tests/StaticResources/Dialect3/Valid/valid_dialect_3_mismatched_name.uif new file mode 100644 index 00000000..8c474993 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Valid/valid_dialect_3_mismatched_name.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="wharrgarbl.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Valid/valid_dialect_3_some_types_in_sections.uif b/Tests/StaticResources/Dialect3/Valid/valid_dialect_3_some_types_in_sections.uif new file mode 100644 index 00000000..2d147c75 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Valid/valid_dialect_3_some_types_in_sections.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="valid_dialect_3.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++board_type="CommonBoard" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 ++type="CommonBox" + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. ++type="CommonMbox" + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/Dialect3/Valid/valid_dialect_3_types_everywhere.uif b/Tests/StaticResources/Dialect3/Valid/valid_dialect_3_types_everywhere.uif new file mode 100644 index 00000000..38a33551 --- /dev/null +++ b/Tests/StaticResources/Dialect3/Valid/valid_dialect_3_types_everywhere.uif @@ -0,0 +1,128 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="valid_dialect_3.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(addr(0),boards(B00,B01,B10,B11,B20,B21)) +El(addr(1),boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010),type("CommonBoard"))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. ++type="CommonBoard" + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00),type(CommonMbox))=M10,M01 +(1,0):M10(addr(01,00),type(CommonMbox))=M00,M20,M11 +(2,0):M20(addr(10,00),type(CommonMbox))=M10,M30,M21 +(3,0):M30(addr(11,00),type(CommonMbox))=M20,M31 +(0,1):M01(addr(00,01),type(CommonMbox))=M00,M11,M02 +(1,1):M11(addr(01,01),type(CommonMbox))=M10,M01,M21,M12 +(2,1):M21(addr(10,01),type(CommonMbox))=M20,M11,M31,M22 +(3,1):M31(addr(11,01),type(CommonMbox))=M30,M21,M32 +(0,2):M02(addr(00,10),type(CommonMbox))=M01,M12,M03 +(1,2):M12(addr(01,10),type(CommonMbox))=M11,M02,M22,M13 +(2,2):M22(addr(10,10),type(CommonMbox))=M21,M12,M32,M23 +(3,2):M32(addr(11,10),type(CommonMbox))=M31,M22,M33 +(0,3):M03(addr(00,11),type(CommonMbox))=M02,M13 +(1,3):M13(addr(01,11),type(CommonMbox))=M12,M03,M23 +(2,3):M23(addr(10,11),type(CommonMbox))=M22,M13,M33 +(3,3):M33(addr(11,11),type(CommonMbox))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_1.uif b/Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_1.uif new file mode 100644 index 00000000..c20c8c4b --- /dev/null +++ b/Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_1.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" +dialect=3 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_bad_dialect_definition_1.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(Boards(B00,B01,B10,B11,B20,B21)) +El(Boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_2.uif b/Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_2.uif new file mode 100644 index 00000000..5dc3d0b8 --- /dev/null +++ b/Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_2.uif @@ -0,0 +1,128 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_bad_dialect_definition_2.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(Boards(B00,B01,B10,B11,B20,B21)) +El(Boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_3.uif b/Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_3.uif new file mode 100644 index 00000000..2b14a818 --- /dev/null +++ b/Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_3.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=4 ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_bad_dialect_definition_3.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(Boards(B00,B01,B10,B11,B20,B21)) +El(Boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_4.uif b/Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_4.uif new file mode 100644 index 00000000..20b7a7b8 --- /dev/null +++ b/Tests/StaticResources/InvalidDialect/invalid_dialect_3_bad_dialect_definition_4.uif @@ -0,0 +1,129 @@ +// A representation of the Byron-Eliot connected pair in Dialect 3. Costs are +// unknown. + +[header(ByronEliot)] ++author="Mark Vousden" ++dialect=(3,1) ++datetime=20190614115500 ++version="0.3.2" ++file="invalid_dialect_3_bad_dialect_definition_4.uif" + +// Sums to 16 bits, leading bits are not used. +[packet_address_format] ++box=1 // The box address component is not used. ++board=(3,3) ++mailbox=(2,2) ++core=2 ++thread=4 + +// All items are of the same type. +[default_types] ++box_type="CommonBox" ++board_type="CommonBoard" ++mailbox_type="CommonMbox" + +// Boxes/boards are connected as follows: +// +// Eliot (= El) +// +-------------------+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +--|------|------|--+ +// | | +// | B01 -- B11 -- B21 | +// | | +// | | | | | +// | | +// | B00 -- B10 -- B20 | +// | | +// +-------------------+ +// Byron (= By) +[engine_box] +By(Boards(B00,B01,B10,B11,B20,B21)) +El(Boards(B00,B01,B10,B11,B20,B21)) ++external_box_cost=0 + +// Note that the Y-component of the board address in hardware comes before the +// X-component. The notation for boards here is, for example: +// +// (Xb,Yb):Box(board(BXY),addr(Y,X)) +// +// where {X,Y} denotes the {horizontal,vertical}-component of the co-ordinate +// in the board system, and {Xb,Yb} denotes the {horizontal,vertical}-component +// of the co-ordinate in the engine system. +[engine_board] +(0,0):By(board(B00),addr(000,000))=By(board(B10)),By(board(B01)) +(1,0):By(board(B10),addr(000,001))=By(board(B00)),By(board(B20)),By(board(B11)) +(2,0):By(board(B20),addr(000,010))=By(board(B10)),By(board(B21)) +(0,1):By(board(B01),addr(001,000))=By(board(B00)),By(board(B11)),El(board(B00)) +(1,1):By(board(B11),addr(001,001))=By(board(B10)),By(board(B01)),By(board(B21)),El(board(B10)) +(2,1):By(board(B21),addr(001,010))=By(board(B20)),By(board(B11)),El(board(B20)) +(0,2):El(board(B00),addr(010,000))=By(board(B01)),El(board(B10)),El(board(B01)) +(1,2):El(board(B10),addr(010,001))=By(board(B11)),El(board(B00)),El(board(B20)),El(board(B11)) +(2,2):El(board(B20),addr(010,010))=By(board(B21)),El(board(B10)),El(board(B21)) +(0,3):El(board(B01),addr(011,000))=El(board(B00)),El(board(B11)) +(1,3):El(board(B11),addr(011,001))=El(board(B10)),El(board(B01)),El(board(B21)) +(2,3):El(board(B21),addr(011,010))=El(board(B20)),El(board(B11)) ++board_board_cost=8 // Relative to [board].mailbox_mailbox_cost. + +[box(CommonBox)] ++box_board_cost=0 ++supervisor_memory=10240 + +// Mailboxes are connected within a board as follows: +// +// +--------------------------+ +// | | +// | M03 -- M13 -- M23 -- M33 | +// | | +// | | | | | | +// | | +// | M02 -- M12 -- M22 -- M32 | +// | | +// | | | | | | +// | | +// | M01 -- M11 -- M21 -- M31 | +// | | +// | | | | | | +// | | +// | M00 -- M10 -- M20 -- M30 | +// | | +// +--------------------------+ +[board(CommonBoard)] +(0,0):M00(addr(00,00))=M10,M01 +(1,0):M10(addr(01,00))=M00,M20,M11 +(2,0):M20(addr(10,00))=M10,M30,M21 +(3,0):M30(addr(11,00))=M20,M31 +(0,1):M01(addr(00,01))=M00,M11,M02 +(1,1):M11(addr(01,01))=M10,M01,M21,M12 +(2,1):M21(addr(10,01))=M20,M11,M31,M22 +(3,1):M31(addr(11,01))=M30,M21,M32 +(0,2):M02(addr(00,10))=M01,M12,M03 +(1,2):M12(addr(01,10))=M11,M02,M22,M13 +(2,2):M22(addr(10,10))=M21,M12,M32,M23 +(3,2):M32(addr(11,10))=M31,M22,M33 +(0,3):M03(addr(00,11))=M02,M13 +(1,3):M13(addr(01,11))=M12,M03,M23 +(2,3):M23(addr(10,11))=M22,M13,M33 +(3,3):M33(addr(11,11))=M32,M23 ++board_mailbox_cost=0 ++supervisor_memory=0 ++mailbox_mailbox_cost=0 // Relative to box::board_board_cost ++dram=4096 // MiB, two DDR3 DRAM boards. + +[mailbox(CommonMbox)] ++cores=4 ++mailbox_core_cost=0 ++core_core_cost=0 + +[core] ++threads=16 ++instruction_memory=8 // KiB ++data_memory=0 ++core_thread_cost=0 ++thread_thread_cost=0 diff --git a/Tests/TestDialect1Reader.cpp b/Tests/TestDialect1Reader.cpp new file mode 100644 index 00000000..809517e7 --- /dev/null +++ b/Tests/TestDialect1Reader.cpp @@ -0,0 +1,46 @@ +/* Tests the Dialect 3 reader's semantic checking, and output on success. + * + * Missing files and files with invalid syntax are tested by the + * TestHardwareFileReader test suite. */ + +#define CATCH_CONFIG_MAIN + +#include "catch.hpp" + +#include "HardwareFileReader.h" +#include "HardwareModel.h" + +TEST_CASE("Aesop example does not raise end-to-end", "[Reader]") +{ + P_engine* engine = new P_engine("Test Engine"); + HardwareFileReader reader("../Tests/StaticResources/Dialect1/aesop_dialect_1.uif", + engine); + delete engine; +} + +TEST_CASE("A file with multiple identical valid sections raise a semantics error", "[Reader]") +{ + P_engine* engine = new P_engine("Test Engine"); + HardwareFileReader reader; + reader.load_file("../Tests/StaticResources/Dialect1/duplicate_section_invalid.uif"); + REQUIRE_THROWS_AS(reader.populate_hardware_model(engine), + HardwareSemanticException&); +} + +TEST_CASE("A file with a missing section raises a semantics error", "[Reader]") +{ + P_engine* engine = new P_engine("Test Engine"); + HardwareFileReader reader; + reader.load_file("../Tests/StaticResources/Dialect1/missing_section_invalid.uif"); + REQUIRE_THROWS_AS(reader.populate_hardware_model(engine), + HardwareSemanticException&); +} + +TEST_CASE("A file with an invalid section raises a semantics error", "[Reader]") +{ + P_engine* engine = new P_engine("Test Engine"); + HardwareFileReader reader; + reader.load_file("../Tests/StaticResources/Dialect1/invalid_section.uif"); + REQUIRE_THROWS_AS(reader.populate_hardware_model(engine), + HardwareSemanticException&); +} diff --git a/Tests/TestDialect3Reader.cpp b/Tests/TestDialect3Reader.cpp new file mode 100644 index 00000000..a574dee3 --- /dev/null +++ b/Tests/TestDialect3Reader.cpp @@ -0,0 +1,112 @@ +/* Tests the Dialect 3 reader's semantic checking, and output on success. + * + * Missing files and files with invalid syntax are tested by the + * TestHardwareFileReader test suite. */ + +#define CATCH_CONFIG_MAIN + +#include "catch.hpp" + +#include "HardwareFileReader.h" +#include "HardwareModel.h" + +/* C++98 does not have a cross-platform way of listing directories... grumble + * grumble. */ +std::vector semanticallyValidInputs = { + "8_box.uif", + "valid_dialect_3_mismatched_name.uif", + "valid_dialect_3_some_types_in_sections.uif", + "valid_dialect_3_types_everywhere.uif", + "valid_dialect_3.uif" +}; + +std::vector semanticallyInvalidInputs = { + "invalid_dialect_3_board_name_too_short.uif", + "invalid_dialect_3_box_name_too_long.uif", + "invalid_dialect_3_broken_header_variable_1.uif", + "invalid_dialect_3_broken_header_variable_2.uif", + "invalid_dialect_3_duplicate_section.uif", + "invalid_dialect_3_empty.uif", + "invalid_dialect_3_floating_dram.uif", + "invalid_dialect_3_invalid_character_in_type.uif", + "invalid_dialect_3_missing_board_type.uif", + "invalid_dialect_3_missing_box_type.uif", + "invalid_dialect_3_missing_cost.uif", + "invalid_dialect_3_missing_mailbox_type.uif", + "invalid_dialect_3_missing_mandatory_header_item.uif", + "invalid_dialect_3_missing_mandatory_header_variable_1.uif", + "invalid_dialect_3_missing_mandatory_header_variable_2.uif", + "invalid_dialect_3_missing_packet_length.uif", + "invalid_dialect_3_missing_referenced_board_2.uif", + "invalid_dialect_3_missing_referenced_board.uif", + "invalid_dialect_3_missing_referenced_box.uif", + "invalid_dialect_3_missing_referenced_mailbox_2.uif", + "invalid_dialect_3_missing_referenced_mailbox.uif", + "invalid_dialect_3_missing_reverse_edge_board_definition.uif", + "invalid_dialect_3_missing_reverse_edge_mailbox_definition.uif", + "invalid_dialect_3_missing_section.uif", + "invalid_dialect_3_nonfloat_cost.uif", + "invalid_dialect_3_type_too_long.uif", + "invalid_dialect_3_type_too_short.uif", + "invalid_dialect_3_undefined_type.uif" +}; + +/* Missing files and files with invalid syntax are tested by the Dialect 1 + * reader test suite (they both go through the same syntax-checking bit of + * source. */ +TEST_CASE("Files with valid syntax and semantics do not raise", "[Reader]") +{ + P_engine* engine; + HardwareFileReader* reader; + std::vector::iterator fileName; + std::string fullPath; + + /* Create a fresh reader and engine object for each test. */ + for(fileName=semanticallyValidInputs.begin(); + fileName!=semanticallyValidInputs.end(); + fileName++) + { + printf("Testing %s...\n", (*fileName).c_str()); + + engine = new P_engine("Test Engine"); + reader = new HardwareFileReader(); + + fullPath = "../Tests/StaticResources/Dialect3/Valid/" + *fileName; + reader->load_file(fullPath.c_str()); + reader->populate_hardware_model(engine); + + delete engine; + delete reader; + } +} + +TEST_CASE("Test each semantically-invalid case in turn", "[Reader]") +{ + P_engine* engine; + HardwareFileReader* reader; + std::vector::iterator fileName; + std::string fullPath; + + /* Create a fresh reader and engine object for each test. */ + for(fileName=semanticallyInvalidInputs.begin(); + fileName!=semanticallyInvalidInputs.end(); + fileName++) + { + printf("Testing %s (xfail)...\n", (*fileName).c_str()); + + engine = new P_engine("Test Engine"); + reader = new HardwareFileReader(); + + fullPath = "../Tests/StaticResources/Dialect3/Invalid/" + *fileName; + reader->load_file(fullPath.c_str()); + REQUIRE_THROWS_AS(reader->populate_hardware_model(engine), + HardwareSemanticException&); + + /* Don't need to delete the engine - the reader is responsible for + * doing that on failure. If a bug has been introduced that causes the + * reader not to clean up after itself, Valgrind will find it. + * + * We do need to delete the reader at the end of the loop though. */ + delete reader; + } +} diff --git a/Tests/TestHardwareAddressAndFormat.cpp b/Tests/TestHardwareAddressAndFormat.cpp index c6b76fa4..0f6f2d28 100644 --- a/Tests/TestHardwareAddressAndFormat.cpp +++ b/Tests/TestHardwareAddressAndFormat.cpp @@ -23,24 +23,47 @@ TEST_CASE("Underdefined addresses can be differentiated from fully-defined addre TEST_CASE("Fully-defined addresses can be created from a format and all values", "[Addressing]") { + /* The behaviour of this test is dependent on whether or not we are + * ignoring the box component. */ HardwareAddressFormat myFormat(4, 5, 6, 8, 9); HardwareAddress address(&myFormat, 15, 31, 63, 255, 511); - REQUIRE(address.get_hardware_address() == 4294967295); /* Sorry! - * // If you want to know what that bizarre number is, try dumping with - * address.Dump(); */ - REQUIRE(address.is_fully_defined() == true); - /* While we're here, test that HardwareAddress::as_uint() is a synonym for - HardwareAddress::get_hardware_address(). */ - REQUIRE(address.get_hardware_address() == address.as_uint()); + /* Sorry about these bizarre numbers! If you want to understand them, try + * dumping the address object with address.Dump(). I've even commented it + * for you here: */ + /* address.Dump(); */ + + if (IGNORE_BOX_ADDRESS_COMPONENT) + { + REQUIRE(address.get_hardware_address() == 268435455); + } + else + { + REQUIRE(address.get_hardware_address() == 4294967295); + } + + /* The address is fully defined in either case. */ + REQUIRE(address.is_fully_defined() == true); } TEST_CASE("Invalid address components cannot be set", "[Addressing]") { HardwareAddressFormat myFormat(4, 5, 6, 8, 9); - // 21 > 2 ** 4 - REQUIRE_THROWS_AS(HardwareAddress(&myFormat, 21, 31, 63, 255, 511), + /* 33 > 2 ** 5 */ + REQUIRE_THROWS_AS(HardwareAddress(&myFormat, 15, 33, 63, 255, 511), InvalidAddressException&); + + /* Also test that the box component does/not throw when we are/not + * considering the box component. 17 > 2 ** 4 */ + if (IGNORE_BOX_ADDRESS_COMPONENT) + { + HardwareAddress(&myFormat, 17, 31, 63, 255, 511); + } + else + { + REQUIRE_THROWS_AS(HardwareAddress(&myFormat, 17, 31, 63, 255, 511), + InvalidAddressException&); + } } TEST_CASE("Test that as_uint() is a synonym of get_hardware_address()", "[Addressing]") diff --git a/Tests/TestHardwareFileParser.cpp b/Tests/TestHardwareFileParser.cpp deleted file mode 100644 index e3b0e294..00000000 --- a/Tests/TestHardwareFileParser.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* Tests various behaviours of the hardware addressing mechanism. */ - -#define CATCH_CONFIG_MAIN - -#include "catch.hpp" - -#include "HardwareFileParser.h" -#include "HardwareModel.h" - -TEST_CASE("Files with a valid syntax do not raise", "[Parser]") -{ - HardwareFileParser parser; - parser.load_file("../Tests/StaticResources/valid_test_file.uif"); -} - -TEST_CASE("Files with an invalid syntax raise syntax error", "[Parser]") -{ - HardwareFileParser parser; - REQUIRE_THROWS_AS(parser.load_file( - "../Tests/StaticResources/syntactically_invalid_test_file.uif"), - HardwareSyntaxException&); -} - -TEST_CASE("Files that do not exist raise a file-not-found error", "[Parser]") -{ - HardwareFileParser parser; - - /* Find a file that doesn't exist by starting from "x", and adding "x"s - * until we hit a non-existent file. Would be better with POSIX's stat. */ - std::string filePath; - FILE* testFile; - while (true) - { - testFile = fopen(filePath.c_str(), "r"); - if (testFile) - { - fclose(testFile); - filePath.append("x"); - } - else {break;} - } - - REQUIRE_THROWS_AS(parser.load_file(filePath.c_str()), - HardwareFileNotFoundException&); -} - -TEST_CASE("Attempting to populate before loaded a file raises", "[Parser]") -{ - P_engine* engine = new P_engine("Test Engine"); - HardwareFileParser parser; - REQUIRE_THROWS_AS(parser.populate_hardware_model(engine), - HardwareFileNotLoadedException&); - delete engine; -} - -TEST_CASE("Aesop example does not raise end-to-end", "[Parser]") -{ - P_engine* engine = new P_engine("Test Engine"); - HardwareFileParser parser("../Tests/StaticResources/aesop_dialect_1.uif", - engine); - delete engine; -} - -TEST_CASE("A file with multiple identical valid sections raise a semantics error", "[Parser]") -{ - P_engine* engine = new P_engine("Test Engine"); - HardwareFileParser parser; - parser.load_file("../Tests/StaticResources/duplicate_section_invalid.uif"); - REQUIRE_THROWS_AS(parser.populate_hardware_model(engine), - HardwareSemanticException&); - delete engine; -} - -TEST_CASE("A file with a missing section raises a semantics error", "[Parser]") -{ - P_engine* engine = new P_engine("Test Engine"); - HardwareFileParser parser; - parser.load_file("../Tests/StaticResources/missing_section_invalid.uif"); - REQUIRE_THROWS_AS(parser.populate_hardware_model(engine), - HardwareSemanticException&); - delete engine; -} - -TEST_CASE("A file with an invalid section raises a semantics error", "[Parser]") -{ - P_engine* engine = new P_engine("Test Engine"); - HardwareFileParser parser; - parser.load_file("../Tests/StaticResources/invalid_section.uif"); - REQUIRE_THROWS_AS(parser.populate_hardware_model(engine), - HardwareSemanticException&); - delete engine; -} diff --git a/Tests/TestHardwareFileReader.cpp b/Tests/TestHardwareFileReader.cpp new file mode 100644 index 00000000..8ea0d0e9 --- /dev/null +++ b/Tests/TestHardwareFileReader.cpp @@ -0,0 +1,87 @@ +/* Tests the hardware description file reader for syntax checking. */ + +#define CATCH_CONFIG_MAIN + +#include "catch.hpp" + +#include "HardwareFileReader.h" +#include "HardwareModel.h" + +/* C++98 does not have a cross-platform way of listing directories... grumble + * grumble. */ +std::vector badDialectInputs = { + "invalid_dialect_3_bad_dialect_definition_1.uif", + "invalid_dialect_3_bad_dialect_definition_2.uif", + "invalid_dialect_3_bad_dialect_definition_3.uif", + "invalid_dialect_3_bad_dialect_definition_4.uif" +}; + +TEST_CASE("Files with a valid syntax do not raise", "[Reader]") +{ + HardwareFileReader reader; + reader.load_file("../Tests/StaticResources/Dialect1/valid_test_file.uif"); +} + +TEST_CASE("Files with an invalid syntax raise syntax error", "[Reader]") +{ + HardwareFileReader reader; + REQUIRE_THROWS_AS(reader.load_file( + "../Tests/StaticResources/Dialect1/syntactically_invalid_test_file.uif"), + HardwareSyntaxException&); +} + +TEST_CASE("Files that do not exist raise a file-not-found error", "[Reader]") +{ + HardwareFileReader reader; + + /* Find a file that doesn't exist by starting from "x", and adding "x"s + * until we hit a non-existent file. Would be better with POSIX's stat. */ + std::string filePath; + FILE* testFile; + while (true) + { + testFile = fopen(filePath.c_str(), "r"); + if (testFile) + { + fclose(testFile); + filePath.append("x"); + } + else {break;} + } + + REQUIRE_THROWS_AS(reader.load_file(filePath.c_str()), + HardwareFileNotFoundException&); +} + +TEST_CASE("Attempting to populate before loaded a file raises", "[Reader]") +{ + P_engine* engine = new P_engine("Test Engine"); + HardwareFileReader reader; + REQUIRE_THROWS_AS(reader.populate_hardware_model(engine), + HardwareFileNotLoadedException&); + delete engine; +} + +TEST_CASE("Test each invalid dialect example case in turn", "[Reader]") +{ + P_engine* engine; + HardwareFileReader* reader; + std::vector::iterator fileName; + std::string fullPath; + + /* Create a fresh reader and engine object for each test. */ + for(fileName=badDialectInputs.begin(); + fileName!=badDialectInputs.end(); + fileName++) + { + engine = new P_engine("Test Engine"); + reader = new HardwareFileReader(); + + fullPath = "../Tests/StaticResources/InvalidDialect/" + *fileName; + reader->load_file(fullPath.c_str()); + REQUIRE_THROWS_AS(reader->populate_hardware_model(engine), + HardwareSemanticException&); + + delete reader; + } +} From 2bbe0a6f80930a234fa5d7da04153f833dd9cddd Mon Sep 17 00:00:00 2001 From: AlexRast Date: Thu, 26 Sep 2019 14:48:22 +0100 Subject: [PATCH 08/14] re-updated makefiles and message file that were somehow reverted in the last commit --- Build/gcc/Makefile.executable_prerequisites | 68 ++++++++++++++++++--- Source/Common/CommonBase.h | 1 + Source/OrchestratorMessages.txt | 45 +++++++++++++- 3 files changed, 106 insertions(+), 8 deletions(-) mode change 100644 => 100755 Build/gcc/Makefile.executable_prerequisites diff --git a/Build/gcc/Makefile.executable_prerequisites b/Build/gcc/Makefile.executable_prerequisites old mode 100644 new mode 100755 index 8fa80e05..b8746102 --- a/Build/gcc/Makefile.executable_prerequisites +++ b/Build/gcc/Makefile.executable_prerequisites @@ -79,6 +79,18 @@ HARDWARE_FILE_READER_SOURCES = $(SOURCE_DIR)/OrchBase/HardwareFileReader/Hardwar $(GENERICS_DIR)/uif.cpp \ $(HARDWARE_DEPLOYER_SOURCES) +# Sources used by executables that provide POETS name resolution. +NAME_SERVICES_SOURCES = $(SOURCE_DIR)/NameServer/ABDefs.cpp \ + $(SOURCE_DIR)/NameServer/ABRecord.cpp \ + $(SOURCE_DIR)/NameServer/ABTask.cpp \ + $(SOURCE_DIR)/NameServer/AddressBook.cpp \ + $(SOURCE_DIR)/NameServer/SBase.cpp \ + $(SOURCE_DIR)/OrchBase/CMsg_p.cpp \ + $(SOURCE_DIR)/OrchBase/P_addr.cpp + +# The LogServer message file is a static text file +LOGSERVER_MESSAGE_FILE_ORIGIN = $(SOURCE_DIR)/OrchestratorMessages.txt + # The orchestrate execution script uses a template. ORCHESTRATE_TEMPLATE = ./Resources/orchestrate_template.sh @@ -125,7 +137,12 @@ ROOT_SOURCES = $(SOURCE_DIR)/Root/RootMain.cpp \ $(GENERICS_DIR)/filename.cpp \ $(GENERICS_DIR)/lex.cpp \ $(GENERICS_DIR)/rand.cpp \ - $(SOURCE_DIR)/NameServer/Ns_el.cpp + $(SOURCE_DIR)/Common/Debug.cpp \ + $(SOURCE_DIR)/NameServer/Ns_el.cpp \ + $(SOURCE_DIR)/NameServer/ABDefs.cpp \ + $(SOURCE_DIR)/NameServer/ABRecord.cpp \ + $(SOURCE_DIR)/NameServer/ABTask.cpp \ + $(SOURCE_DIR)/NameServer/AddressBook.cpp ROOT_SOURCES += $(TRULY_COMMON_SOURCES) ROOT_SOURCES += $(HARDWARE_FILE_READER_SOURCES) @@ -178,6 +195,26 @@ LOGSERVER_SOURCES += $(TRULY_COMMON_SOURCES) # from which the Orchestrator is started. LOGSERVER_MESSAGE_FILE_ORIGIN = $(SOURCE_DIR)/OrchestratorMessages.txt +# The nameserver component consists of: +# +# - The "main" file (/Source/NameServer/NameServerMain.cpp) and its derived +# class in /Source/NameServer. +# +# - The name services sources in /Source/NameServer. +# +# - The truly common sources. +# +# - The hardware and software address sources. +# +NAMESERVER_SOURCES = $(SOURCE_DIR)/NameServer/NameServerMain.cpp \ + $(SOURCE_DIR)/NameServer/NameServer.cpp \ + $(SOURCE_DIR)/Common/Debug.cpp + +NAMESERVER_SOURCES += $(NAME_SERVICES_SOURCES) +NAMESERVER_SOURCES += $(TRULY_COMMON_SOURCES) +#NAMESERVER_SOURCES += $(SOFTWARE_ADDRESS_SOURCES) +#NAMESERVER_SOURCES += $(HARDWARE_ADDRESS_SOURCES) + # The rtcl (real-time clock) component consists of: # # - The "main" file (/Source/RTCL/RTCL.cpp) and its dependencies in @@ -220,6 +257,8 @@ INJECTOR_SOURCES += $(TRULY_COMMON_SOURCES) # # - An interface source file from the softswitch, in /Source/Softswitch/src. # +# - The name services sources. +# # - The truly common sources. # # - Some "generics" (/Generics) sources. @@ -229,25 +268,40 @@ INJECTOR_SOURCES += $(TRULY_COMMON_SOURCES) # Hostlink sources are to be compiled separately. MOTHERSHIP_SOURCES = $(SOURCE_DIR)/Mothership/MothershipMain.cpp \ $(SOURCE_DIR)/Mothership/TaskInfo.cpp \ - $(SOURCE_DIR)/Mothership/TMoth.cpp \ + $(SOURCE_DIR)/Mothership/Mothership.cpp \ $(SOURCE_DIR)/Softswitch/src/poets_msg.cpp \ $(GENERICS_DIR)/dumpchan.cpp \ $(GENERICS_DIR)/rand.cpp \ + $(SOURCE_DIR)/Common/Debug.cpp \ $(SOURCE_DIR)/OrchBase/Bin.cpp \ $(SOURCE_DIR)/OrchBase/build_defs.cpp \ $(SOURCE_DIR)/OrchBase/CFrag.cpp \ - $(SOURCE_DIR)/OrchBase/CMsg_p.cpp \ $(SOURCE_DIR)/OrchBase/D_graph.cpp \ - $(SOURCE_DIR)/OrchBase/P_addr.cpp \ $(SOURCE_DIR)/OrchBase/P_device.cpp \ $(SOURCE_DIR)/OrchBase/P_message.cpp \ $(SOURCE_DIR)/OrchBase/P_pin.cpp \ $(SOURCE_DIR)/OrchBase/P_task.cpp \ - $(SOURCE_DIR)/Common/Debug.cpp \ $(HARDWARE_DEPLOYER_SOURCES) +MOTHERSHIP_SOURCES += $(NAME_SERVICES_SOURCES) MOTHERSHIP_SOURCES += $(TRULY_COMMON_SOURCES) +# A dummy mothership is an executable to provide mothership-like functionality +# when there is no connected box with a HostLink interface. It consists of: +# +# - The "main" file (/Source/Mothership/MothershipDummyMain.cpp) and its +# dependencies in /Source/Mothership +# +# - The name services sources. +# +# - The truly common sources. +# +MOTHERSHIP_DUMMY_SOURCES = $(SOURCE_DIR)/Mothership/MothershipDummyMain.cpp \ + $(SOURCE_DIR)/Mothership/MothershipDummy.cpp + +MOTHERSHIP_DUMMY_SOURCES += $(NAME_SERVICES_SOURCES) +MOTHERSHIP_DUMMY_SOURCES += $(TRULY_COMMON_SOURCES) + # Convert these lists of sources into a list of objects to define a dependency # system for the linker. @@ -279,7 +333,7 @@ $(1)_OBJECTS := $(call orch_sources_to_objects, $($(1)_SOURCES)) endef $(foreach object_set,\ - LAUNCHER ROOT INJECTOR DUMMY LOGSERVER RTCL MOTHERSHIP,\ + LAUNCHER ROOT INJECTOR DUMMY LOGSERVER NAMESERVER RTCL MOTHERSHIP MOTHERSHIP_DUMMY,\ $(eval $(call OBJECT_TEMPLATE,$(object_set)))) # Define executable prerequisites. RULE_TEMPLATE defines the substitutions made @@ -292,5 +346,5 @@ $($(1)_EXECUTABLE): $($(1)_OBJECTS) endef $(foreach executable_name,\ - LAUNCHER ROOT DUMMY LOGSERVER RTCL INJECTOR MOTHERSHIP,\ + LAUNCHER ROOT DUMMY LOGSERVER NAMESERVER RTCL INJECTOR MOTHERSHIP MOTHERSHIP_DUMMY,\ $(eval $(call RULE_TEMPLATE,$(executable_name)))) diff --git a/Source/Common/CommonBase.h b/Source/Common/CommonBase.h index e1222545..b51cf4ef 100644 --- a/Source/Common/CommonBase.h +++ b/Source/Common/CommonBase.h @@ -5,6 +5,7 @@ #include #include using namespace std; +#include "Debug.h" #include "PMsg_p.hpp" #include "ProcMap.h" #include "pthread.h" diff --git a/Source/OrchestratorMessages.txt b/Source/OrchestratorMessages.txt index 348aa1f6..edefdd80 100644 --- a/Source/OrchestratorMessages.txt +++ b/Source/OrchestratorMessages.txt @@ -86,7 +86,7 @@ 515(W) : "Task %s not mapped to this mothership (%s)" 516(E) : "Boot thread could not be created for board %s for task %s" 517(W) : "Task %s could not be stopped: state is %s" -518(E) : "Unable to boot task completely: %s cores loaded, %s cores expected - aborting" +518(E) : "Unable to boot task %s completely: %s cores loaded, %s cores expected - aborting" 520(W) : "Hardware command ||%s|| to mothership %s failed" 520(W) : "Unrecognised Mothership system command %s" 530(E) : "Tinsel packet not received successfully on mothership %s" @@ -95,11 +95,54 @@ 533(E) : "Bad symbol %s in the Supervisor library on Mothership %s: check XML definition. Error was %s" 534(E) : "Supervisor library could not be successfully unloaded on Mothership %s. Future attempts to run tasks may have strange I/O behaviour" 540(I) : "Application Supervisor being started on Mothership %s" +550(E) : "Unexpected name services state configuration received on Mothership %s. Asked for state %s, actual state is %s" +560(I) : "Task %s booting..." +561(I) : "Task %s ready to be run" +562(I) : "Loading core %s on box %s, board %s" +563(I) : "%s of %s cores loaded. Loading board %s of %s" 600(I) : "%s" 601(I) : "Core %s: (part %s): %s" 607(W) : "Attempted to recall a task %s that was not deployed to the Mothership" +700(W) : "Unrecognised command %s to SBase process rank %s" +701(U) : "Bad command switch in SBase process rank %s" +702(W) : "Unrecognised query subtype %s to SBase process rank %s" +703(E) : "Unexpected reply to query: %s received at SBase process rank %s. Circular routing loop?" +704(E) : "Tried to load task into SBase at rank %s with no name" +705(E) : "Error while loading SBase data at rank %s: no task name" +706(E) : "Error %sing device type data for task %s in SBase at rank %s: no device type name" +707(E) : "Error loading device %s for task %s into SBase at rank %s" +708(E) : "Error loading device %s for task %s into SBase at rank %s: no such task" +709(W) : "Unable to register supervisor %s on box %s into SBase: not found in the rank list. May need to connect" +710(W) : "Empty task for SBase %s request at rank %s" +711(S) : "Error: no nameserver found" +712(U) : "Fatal error: nameserver on nonexistent comm" +713(I) : "Sending information to nameserver rank %s from root rank %s on devices on thread %s" +714(I) : "Sending information to nameserver rank %s from root rank %s on supervisors for task %s" +715(I) : "SBase rank %s received information from rank %s on devices for task %s" +716(E) : "Mismatch in device name-data info to SBase at rank %s: %s device names, %s %ss received" +717(E) : "Error sending device data to SBase at rank %s for device %s: invalid record type %s" +718(E) : "Error: no supervisor found for device records for task %s on SBase at rank %s" +719(E) : "Error loading device records for task %s on SBase at rank %s: no such device type %s" +720(W) : "Mismatch in number of device counts sent: %s, expected %s for task %s to SBase at rank %s" +721(E) : "Error %sing task %s on SBase at rank %s" +723(W) : "Task binary directory path to SBase at rank %s is empty. Current working directory will be used" +724(E) : "No task %s on SBase at rank %s" +725(E) : "Error attempting to set state for task %s on SBase at rank %s" +726(I) : "Deploying data to Mothership at rank %s: %s cores distributed" +727(W) : "SBase map on rank %s is invalid. Rebuild before performing tasks on this SBase" +728(E) : "Error: SBase at rank %s has a record for Supervisor rank %s, but no such Supervisor exists" +729(E) : "Supervisor %s registered as a device for Supervisor %s, but only one Supervisor can exist" +730(W) : "Invalid command %s to SBase rank %s: missing value %s" +731(E) : "Query made for a nameless or nonexistent device on SBase rank %s" +732(S) : "SBase at rank %s shows a Supervisor %s for device %s, but no associated %s" +733(E) : "Error looking up by message type on SBase at rank %s: no message type given" +734(W) : "Task %s is in an unknown state according to SBase at rank %s" +735(E) : "No device address received for device name %s to give to process at rank %s" +736(E) : "No associated supervisor address located for device %s to give to process at rank %s" +737(E) : "Mismatch in supervisor data received at rank %s from SBase: %s addresses, %s names, %s ranks" + 801(D) : "P_builder::Add(name=%s,file=%s)" 802(D) : "Removing task %s from the Orchestrator" 803(F) : "P_builder:out of memory to create a QCoreApplication object" From c90afb9dddab427275bc81654ae1d90db7be599d Mon Sep 17 00:00:00 2001 From: AlexRast Date: Thu, 26 Sep 2019 15:15:25 +0100 Subject: [PATCH 09/14] OK, this probably fixes merge mismatches --- Build/gcc/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Build/gcc/Makefile b/Build/gcc/Makefile index a107be96..77dc07ad 100644 --- a/Build/gcc/Makefile +++ b/Build/gcc/Makefile @@ -22,6 +22,10 @@ MKDIR = mkdir --parents # incorporate your additional sources: include Makefile.dependencies +# Path to the root directory of the Orchestrator (not to be confused with the +# root process of the Orchestrator). +ROOT_DIR = ../.. + # Where sources live. GENERICS_DIR := $(ROOT_DIR)/Generics SOURCE_DIR := $(ROOT_DIR)/Source From 2eba942b884b7c7a8de67155ca0d3659b395f5e5 Mon Sep 17 00:00:00 2001 From: AlexRast Date: Tue, 1 Oct 2019 00:38:08 +0100 Subject: [PATCH 10/14] Fixing merge issues: warnings removed, duplicate makefile lines gone, tabs replaced with spaces --- Build/gcc/Makefile | 4 ---- Build/gcc/Makefile.executable_prerequisites | 19 ++++++++----------- Source/Common/PMsg_p.cpp | 2 +- Source/Mothership/Mothership.cpp | 3 ++- Source/NameServer/NameServer.cpp | 14 +++++++------- Source/OrchBase/CMsg_p.h | 18 +++++++++--------- Source/OrchBase/OrchBaseLink.cpp | 2 +- 7 files changed, 28 insertions(+), 34 deletions(-) diff --git a/Build/gcc/Makefile b/Build/gcc/Makefile index 77dc07ad..a107be96 100644 --- a/Build/gcc/Makefile +++ b/Build/gcc/Makefile @@ -22,10 +22,6 @@ MKDIR = mkdir --parents # incorporate your additional sources: include Makefile.dependencies -# Path to the root directory of the Orchestrator (not to be confused with the -# root process of the Orchestrator). -ROOT_DIR = ../.. - # Where sources live. GENERICS_DIR := $(ROOT_DIR)/Generics SOURCE_DIR := $(ROOT_DIR)/Source diff --git a/Build/gcc/Makefile.executable_prerequisites b/Build/gcc/Makefile.executable_prerequisites index b8746102..ef269af9 100755 --- a/Build/gcc/Makefile.executable_prerequisites +++ b/Build/gcc/Makefile.executable_prerequisites @@ -84,12 +84,9 @@ NAME_SERVICES_SOURCES = $(SOURCE_DIR)/NameServer/ABDefs.cpp \ $(SOURCE_DIR)/NameServer/ABRecord.cpp \ $(SOURCE_DIR)/NameServer/ABTask.cpp \ $(SOURCE_DIR)/NameServer/AddressBook.cpp \ - $(SOURCE_DIR)/NameServer/SBase.cpp \ - $(SOURCE_DIR)/OrchBase/CMsg_p.cpp \ - $(SOURCE_DIR)/OrchBase/P_addr.cpp - -# The LogServer message file is a static text file -LOGSERVER_MESSAGE_FILE_ORIGIN = $(SOURCE_DIR)/OrchestratorMessages.txt + $(SOURCE_DIR)/NameServer/SBase.cpp \ + $(SOURCE_DIR)/OrchBase/CMsg_p.cpp \ + $(SOURCE_DIR)/OrchBase/P_addr.cpp # The orchestrate execution script uses a template. ORCHESTRATE_TEMPLATE = ./Resources/orchestrate_template.sh @@ -137,9 +134,9 @@ ROOT_SOURCES = $(SOURCE_DIR)/Root/RootMain.cpp \ $(GENERICS_DIR)/filename.cpp \ $(GENERICS_DIR)/lex.cpp \ $(GENERICS_DIR)/rand.cpp \ - $(SOURCE_DIR)/Common/Debug.cpp \ + $(SOURCE_DIR)/Common/Debug.cpp \ $(SOURCE_DIR)/NameServer/Ns_el.cpp \ - $(SOURCE_DIR)/NameServer/ABDefs.cpp \ + $(SOURCE_DIR)/NameServer/ABDefs.cpp \ $(SOURCE_DIR)/NameServer/ABRecord.cpp \ $(SOURCE_DIR)/NameServer/ABTask.cpp \ $(SOURCE_DIR)/NameServer/AddressBook.cpp @@ -208,8 +205,8 @@ LOGSERVER_MESSAGE_FILE_ORIGIN = $(SOURCE_DIR)/OrchestratorMessages.txt # NAMESERVER_SOURCES = $(SOURCE_DIR)/NameServer/NameServerMain.cpp \ $(SOURCE_DIR)/NameServer/NameServer.cpp \ - $(SOURCE_DIR)/Common/Debug.cpp - + $(SOURCE_DIR)/Common/Debug.cpp + NAMESERVER_SOURCES += $(NAME_SERVICES_SOURCES) NAMESERVER_SOURCES += $(TRULY_COMMON_SOURCES) #NAMESERVER_SOURCES += $(SOFTWARE_ADDRESS_SOURCES) @@ -272,7 +269,7 @@ MOTHERSHIP_SOURCES = $(SOURCE_DIR)/Mothership/MothershipMain.cpp \ $(SOURCE_DIR)/Softswitch/src/poets_msg.cpp \ $(GENERICS_DIR)/dumpchan.cpp \ $(GENERICS_DIR)/rand.cpp \ - $(SOURCE_DIR)/Common/Debug.cpp \ + $(SOURCE_DIR)/Common/Debug.cpp \ $(SOURCE_DIR)/OrchBase/Bin.cpp \ $(SOURCE_DIR)/OrchBase/build_defs.cpp \ $(SOURCE_DIR)/OrchBase/CFrag.cpp \ diff --git a/Source/Common/PMsg_p.cpp b/Source/Common/PMsg_p.cpp index 810b2427..19c4f438 100644 --- a/Source/Common/PMsg_p.cpp +++ b/Source/Common/PMsg_p.cpp @@ -12,7 +12,7 @@ and when I come across a need. PMsg_p::PMsg_p(MPI_Comm c):Msg_p(){comm=c;} PMsg_p::PMsg_p(byte * pb,int l,MPI_Comm c):Msg_p(pb,l){comm=c;} -PMsg_p::PMsg_p(Msg_p & m, MPI_Comm c):Msg_p(m){comm=c;}; +PMsg_p::PMsg_p(Msg_p & m, MPI_Comm c):Msg_p(m){comm=c;} PMsg_p::PMsg_p(PMsg_p & r):Msg_p(r){comm=r.comm;} PMsg_p::~PMsg_p(void){} diff --git a/Source/Mothership/Mothership.cpp b/Source/Mothership/Mothership.cpp index 0a2f2e31..529c20fc 100644 --- a/Source/Mothership/Mothership.cpp +++ b/Source/Mothership/Mothership.cpp @@ -1286,7 +1286,7 @@ void* Mothership::Twig(void* par) { Mothership* parent = static_cast(par); const uint32_t szFlit = (1<(recv_buf); FILE* OutFile; char Line[4*P_MSG_MAX_SIZE]; @@ -1413,6 +1413,7 @@ void* Mothership::Twig(void* par) } } DebugPrint("Exiting Twig thread\n"); + delete[] recv_buf; pthread_exit(par); return par; } diff --git a/Source/NameServer/NameServer.cpp b/Source/NameServer/NameServer.cpp index e9c7b7d3..038d679b 100644 --- a/Source/NameServer/NameServer.cpp +++ b/Source/NameServer/NameServer.cpp @@ -158,9 +158,9 @@ unsigned NameServer::ConfigDir(PMsg_p * msg, unsigned comm) break; } } - if (msg->Tgt() == s->Rank) break; // supervisor found + if (msg->Tgt() == static_cast(s->Rank)) break; // supervisor found } - if (msg->Tgt() != s->Rank) // supervisor not found + if (msg->Tgt() != static_cast(s->Rank)) // supervisor not found { Post(728,int2str(Urank),uint2str(s->Rank)); return ERR_INVALID_SUPERVISOR; @@ -209,7 +209,7 @@ unsigned NameServer::ConfigDistribute(PMsg_p * msg, unsigned comm) taskInfo.Src(Urank); taskInfo.Key(Q::NAME,Q::DATA,Q::TASK); TaskData_t taskData; - if (err = GetTask(taskName,taskData)) + if ((err = GetTask(taskName,taskData))) { Post(724,taskName,int2str(Urank)); return err; @@ -253,7 +253,7 @@ unsigned NameServer::ConfigDistribute(PMsg_p * msg, unsigned comm) devAttrs.clear(); superRanks.clear(); const RecordVect_t* devicesThisSuper; - if (err = FindBySuper(taskName,s->Address,devicesThisSuper)) + if ((err = FindBySuper(taskName,s->Address,devicesThisSuper))) { switch (err) { @@ -362,9 +362,9 @@ unsigned NameServer::ConfigDistribute(PMsg_p * msg, unsigned comm) break; } } - if (msg->Tgt() == s->Rank) break; // supervisor found + if (msg->Tgt() == static_cast(s->Rank)) break; // supervisor found } - if (msg->Tgt() != s->Rank) // supervisor not found + if (msg->Tgt() != static_cast(s->Rank)) // supervisor not found { Post(728,int2str(Urank),uint2str(s->Rank)); return ERR_INVALID_SUPERVISOR; @@ -377,7 +377,7 @@ unsigned NameServer::ConfigDistribute(PMsg_p * msg, unsigned comm) distMsg.Dump(nsOutput); fclose(nsOutput); //------------------------------------------------- - if (err = SBase::ConfigDir(msg,comm)) // distribution messages contain the binary directory + if ((err = SBase::ConfigDir(msg,comm))) // distribution messages contain the binary directory { Post(724,taskName,int2str(Urank)); // no task of this name. return err; // this should have been caught earlier. diff --git a/Source/OrchBase/CMsg_p.h b/Source/OrchBase/CMsg_p.h index 8935b2e4..9aea69c9 100644 --- a/Source/OrchBase/CMsg_p.h +++ b/Source/OrchBase/CMsg_p.h @@ -16,19 +16,19 @@ class CMsg_p : public PMsg_p void Dump(FILE * = stdout); template void Dump(FILE * = stdout); - template inline T * Get(int k,int & cnt) {return k ? (T*)0 : PMsg_p::Get(k,cnt);}; + template inline T * Get(int k,int & cnt) {return k ? (T*)0 : PMsg_p::Get(k,cnt);} pair * Get(int &); - inline void Get(int k,string & s) {return;}; - template inline void Get(int k,vector & vT) {return;}; + inline void Get(int k,string & s) {return;} + template inline void Get(int k,vector & vT) {return;} void Get(vector > &); - inline void GetX(int k,vector & vs) {return;}; - template inline int Put() {return -1;}; - template void Put(int k,T * data,int cnt=1) {if (!k) PMsg_p::Put(k,data,cnt);}; + inline void GetX(int k,vector & vs) {return;} + template inline int Put() {return -1;} + template void Put(int k,T * data,int cnt=1) {if (!k) PMsg_p::Put(k,data,cnt);} void Put(pair *,int=1); - inline void Put(int k,string * data) {return;}; - template inline void Put(int k,vector * data) {return;}; + inline void Put(int k,string * data) {return;} + template inline void Put(int k,vector * data) {return;} void Put(vector > *); - inline void PutX(int k,vector * data) {return;}; + inline void PutX(int k,vector * data) {return;} }; diff --git a/Source/OrchBase/OrchBaseLink.cpp b/Source/OrchBase/OrchBaseLink.cpp index 63f78b7d..b3d23b19 100644 --- a/Source/OrchBase/OrchBaseLink.cpp +++ b/Source/OrchBase/OrchBaseLink.cpp @@ -257,7 +257,7 @@ if (!pPlace->Place(pT)) { vector devNames; vector devAddrs; - SymAddr_t devSuper; + // SymAddr_t devSuper; // any given message will only contain one device type, corresponding // to the one used in a particular thread. string devTyp = thread->second->P_devicel.front()->pP_devtyp->Name(); From cd819693e187e853ab4bf2fb4abfe2936a05fd3a Mon Sep 17 00:00:00 2001 From: AlexRast Date: Tue, 1 Oct 2019 16:50:08 +0100 Subject: [PATCH 11/14] corrected most of MLV's crucial comments except for the one regarding NameServer searching --- Source/Mothership/Mothership.cpp | 71 ++++++++++++++++++-------------- Source/Mothership/Mothership.h | 24 +++++------ Source/OrchBase/OrchBaseLink.cpp | 32 ++++---------- Source/OrchestratorMessages.txt | 2 + 4 files changed, 62 insertions(+), 67 deletions(-) diff --git a/Source/Mothership/Mothership.cpp b/Source/Mothership/Mothership.cpp index 529c20fc..75eaf857 100644 --- a/Source/Mothership/Mothership.cpp +++ b/Source/Mothership/Mothership.cpp @@ -157,6 +157,8 @@ unsigned Mothership::Boot(string task) DebugPrint("Booting %d threads on threadID 0x%X at x:%d y:%d c:%d\n", (*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress), mX,mY,core); + DebugPrint("* startOne(meshX=%u, meshY=%u, coreId=%u, numThreads=%lu)\n", + mX, mY, core, (*C)->P_threadm.size()); startOne(mX,mY,core,(*C)->P_threadm.size()); DebugPrint("%d threads started on threadID 0x%X at x:%d y:%d c:%d\n", (*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress), @@ -164,6 +166,7 @@ unsigned Mothership::Boot(string task) DebugPrint("Triggering %d threads on core %d at x:%d y:%d c:%d\n", (*C)->P_threadm.size(),Mothership::GetHWAddr(coreAddress), mX,mY,core); + DebugPrint("* goOne(meshX=%u, meshY=%u, coreId=%u)\n", mX, mY, core); goOne(mX,mY,core); } // per Matt Naylor comment safer to start all cores then issue the go command to all cores separately. @@ -174,7 +177,7 @@ unsigned Mothership::Boot(string task) // goOne(mX,mY,core); // } // DebugPrint("%d cores booted\n", taskCores.size()); - + // deal with the barrier. Keep receiving messages until the bitmap is clear. P_Sup_Msg_t barrier_msg; while (!t_start_bitmap.empty()) @@ -191,7 +194,7 @@ unsigned Mothership::Boot(string task) unsigned hw_core = toAddr(mX,mY,core,0); DebugPrint("Received a barrier acknowledge from core %#X\n", hw_core); // valid core? - if (t_start_bitmap.find(hw_core) != t_start_bitmap.end()) + if (t_start_bitmap.find(hw_core) != t_start_bitmap.end()) { DebugPrint("Thread %d on core %d responding\n", thread, core); DebugPrint("Core bitmap for thread %d before acknowledge: %#X\n", @@ -289,7 +292,7 @@ unsigned Mothership::CmLoad(string task) DebugPrint("%d tasks deployed to Mothership\n", TaskMap.size()); if (TaskMap.find(task) == TaskMap.end()) // task deployed? { - Post(515, task, int2str(Urank)); // No. Abandon. + Post(515, task, int2str(Urank)); // No. Abandon. return 0; } DebugPrint("Task %s found\n", task.c_str()); @@ -436,7 +439,7 @@ unsigned Mothership::CmRun(string task) WALKVECTOR(unsigned,threadsToRelease,R) { // send to every device on the thread with a supervisor message - barrier_msg.destDeviceAddr = DEST_BROADCAST; + barrier_msg.destDeviceAddr = DEST_BROADCAST; DebugPrint("Attempting to send barrier release message to the thread " "with hardware address %u.\n", *R); // this is the 'magic invocation' to release the Tinsel barrier @@ -473,7 +476,7 @@ unsigned Mothership::CmStop(string task) case TaskInfo_t::TASK_END: Post(511, task,"stopped", TaskInfo_t::Task_Status.find(TaskMap[task]->status)->second); - return 0; + return 0; case TaskInfo_t::TASK_BOOT: case TaskInfo_t::TASK_RDY: case TaskInfo_t::TASK_STOP: @@ -574,14 +577,14 @@ unsigned Mothership::ConfigDistribute(PMsg_p* msg, unsigned comm) DebugPrint("About to set binary directory %s for task %s\n", msg->Zname(1).c_str(),msg->Zname(0).c_str()); // set the directory for core binaries - return ConfigDir(msg, comm); + return ConfigDir(msg, comm); } //------------------------------------------------------------------------------ unsigned Mothership::ConfigRecall(PMsg_p* msg, unsigned comm) // remove (recall) a core info block from the Mothership. This frees up all -// resources owned by a given task to be reallocatable to another task. +// resources owned by a given task to be reallocatable to another task. { string TaskName = msg->Zname(0); map::iterator T; @@ -626,7 +629,7 @@ unsigned Mothership::ConfigRecall(PMsg_p* msg, unsigned comm) TaskMap[*tName]->status = TaskInfo_t::TASK_ERR; Post(812, *tName); return AddressBook::ERR_INVALID_TASK; - } + } } // ends switch (TaskMap[*tName]->status) system((string("rm -r -f ")+TaskMap[*tName]->BinPath).c_str()); delete TaskMap[*tName]; // get rid of its TaskInfo object @@ -642,7 +645,7 @@ unsigned Mothership::ConfigDir(PMsg_p *msg, unsigned comm) // issued as a separate command: task /xpath, although usually task /deploy // sets this up. { - string task = msg->Zname(0); // task name is in the first static field + string task = msg->Zname(0); // task name is in the first static field string dir = msg->Zname(1); // and the binary path is in the second DebugPrint("Name config dir command received: task %s, directory %s\n", task.c_str(), dir.c_str()); @@ -665,15 +668,15 @@ unsigned Mothership::ConfigState(PMsg_p *msg, unsigned comm) A Mothership should never receive a state-configuration name message. It should only send them, or update the state internally and call the SBase method from the internal update. - */ + */ // get the name to distinguish the type of error - string taskName = msg->Zname(0); + string taskName = msg->Zname(0); if (taskName.empty()) Post(710, "ConfigState", int2str(Urank)); // no task name - else if (TaskMap.find(taskName) == TaskMap.end()) + else if (TaskMap.find(taskName) == TaskMap.end()) Post(715, taskName, int2str(Urank)); // task not loaded to this Mothership // find the task and warn the user that they are attempting something illegal - else + else { map::const_iterator st = TaskInfo_t::Task_Status.find(TaskMap[taskName]->status); @@ -684,7 +687,7 @@ unsigned Mothership::ConfigState(PMsg_p *msg, unsigned comm) } else Post(550, int2str(Urank), msg->Zname(1), st->second); } - return AddressBook::ERR_NONFATAL; // whatever the case we ignore the message + return AddressBook::ERR_NONFATAL; // whatever the case we ignore the message } //------------------------------------------------------------------------------ @@ -701,7 +704,7 @@ unsigned Mothership::Connect(string svc) */ unsigned connErr = MPI_SUCCESS; // set up the connection in the base class - if ((connErr = SBase::Connect(svc)) != MPI_SUCCESS) return connErr; + if ((connErr = SBase::Connect(svc)) != MPI_SUCCESS) return connErr; FnMapx.push_back(new FnMap_t); // add another function table in the derived class int fIdx=FnMapx.size()-1; (*FnMapx[fIdx])[PMsg_p::KEY(Q::EXIT )] = &Mothership::OnExit; // overloads CommonBase @@ -778,7 +781,7 @@ else if (task == "*") unsigned err = AddressBook::SUCCESS; if ((err = ListTask(tasks))) // get all the tasks { - Post(710, "Dump", int2str(Urank)); // error: no tasks were found + Post(710, "Dump", int2str(Urank)); // error: no tasks were found if (fp != stdout) fclose(fp); return; // so we don't need to do anything } @@ -792,7 +795,7 @@ else SBase::Dump(fp,task); fprintf(fp,"SBase dump-----------------------------------\n"); } - + fprintf(fp,"Mothership dump-----------------------------------\n"); fflush(fp); CommonBase::Dump(fp); @@ -842,7 +845,7 @@ long Mothership::LoadBoard(P_board* board) 2 separate binaries, a data binary and an executable image, loaded into different regions as specified in the linker maps generated by the softswitch builder. Each thread has its own private data space in SDRAM; - each core has a private instruction SRAM, although depending upon + each core has a private instruction SRAM, although depending upon configuration 2 cores - an even and odd-numbered pair, may share instruction memory (and have to have the same image) */ @@ -901,8 +904,16 @@ long Mothership::LoadBoard(P_board* board) &thread); DebugPrint("Loading hardware thread 0x%X at x:%d y:%d c:%d\n", Mothership::GetHWAddr(coreAddress), mX, mY, core); + // Load instruction memory, then data memory. + DebugPrint("* loadInstrsOntoCore(codeFilename=%s, meshX=%u, " + "meshY=%u, coreId=%u)\n", + code_f.c_str(), mX, mY, core); loadInstrsOntoCore(code_f.c_str(), mX, mY, core); + DebugPrint("* loadDataViaCore(dataFilename=%s, meshX=%u, " + "meshY=%u, coreId=%u)\n", + data_f.c_str(), mX, mY, core); + loadDataViaCore(data_f.c_str(), mX, mY, core); ++coresLoaded; } @@ -921,10 +932,10 @@ unsigned Mothership::OnCfg(PMsg_p *msg, unsigned comm) // top-level instruction dispatch for Q::NAME, Q::CFG command subset switch(msg->L(2)) { - case Q::DIST: // DIST and TDIR both set the binary directory; + case Q::DIST: // DIST and TDIR both set the binary directory; return ConfigDistribute(msg, comm); // DIST also loads the core map - case Q::TDIR: - return ConfigDir(msg, comm); + case Q::TDIR: + return ConfigDir(msg, comm); case Q::BLD: return SBase::ConfigBuild(msg, comm); // BLD rebuilts SBase maps case Q::RECL: @@ -980,7 +991,7 @@ unsigned Mothership::OnDump(PMsg_p *msg, unsigned comm) default: Post(700,uint2str(msg->Key()),int2str(Urank)); return 0; - } + } } //------------------------------------------------------------------------------ @@ -1332,27 +1343,27 @@ void* Mothership::Twig(void* par) // debug message/watchdog: announces that this thread is // still operational. - if (s_hdr->command == P_PKT_MSGTYP_ALIVE) + if (s_hdr->command == P_PKT_MSGTYP_ALIVE) { DebugPrint("Thread %d is still alive\n", s_hdr->sourceDeviceAddr >> P_THREAD_OS); } else { //------------------------------------------------------------------------------ - // other messages are routed to the supervisor + // other messages are routed to the supervisor DebugPrint("Message is a Supervisor request from device %d\n", s_hdr->sourceDeviceAddr); // new device talking? - if (parent->TwigMap[s_hdr->sourceDeviceAddr] == 0) + if (parent->TwigMap[s_hdr->sourceDeviceAddr] == 0) { - // supervisors have their own internal device buffers + // supervisors have their own internal device buffers // (pins) for each device DebugPrint("New device %d reporting to Supervisor\n", s_hdr->sourceDeviceAddr); parent->TwigMap[s_hdr->sourceDeviceAddr] = new PinBuf_t; } // inactive pin for the device? - if ((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] == 0) + if ((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin] == 0) { DebugPrint("New pin %d for device %d reporting to Supervisor\n", s_hdr->destPin, s_hdr->sourceDeviceAddr); @@ -1360,11 +1371,11 @@ void* Mothership::Twig(void* par) new char[MAX_P_SUP_MSG_BYTES](); } // convert the buffer into a supervisor message - P_Sup_Msg_t* recvdMsg = + P_Sup_Msg_t* recvdMsg = static_cast(static_cast ((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin])); // stuff header into the persistent buffer - memcpy(recvdMsg+s_hdr->seq,s_hdr,p_sup_hdr_size()); + memcpy(recvdMsg+s_hdr->seq,s_hdr,p_sup_hdr_size()); DebugPrint("Expecting message of total length %d\n", s_hdr->cmdLenBytes); // more message to receive? @@ -1400,7 +1411,7 @@ void* Mothership::Twig(void* par) fgetpos(OutFile, &writePos); } // or possibly only dumped to the local console - else while (parent->pollStdOut()); + else while (parent->pollStdOut()); // output the debug output buffer, which has to be done immediately // because we are in a separate thread if (OutFile && updated) diff --git a/Source/Mothership/Mothership.h b/Source/Mothership/Mothership.h index 03753634..29e70efc 100644 --- a/Source/Mothership/Mothership.h +++ b/Source/Mothership/Mothership.h @@ -14,8 +14,8 @@ //============================================================================== /* A Mothership is a process resident on a Box - a PC directly connected to the - tinsel network using HostLink. Motherships combine several pieces of - functionality, deriving from 2 classes. + tinsel network using HostLink. Motherships combine several pieces of + functionality, deriving from 2 classes. From SBase, Motherships handle local name services and receive a subset of the name server data related to the devices mapped to hardware resources directly managed by this Mothership. SBase itself inherits from CommonBase @@ -24,11 +24,11 @@ Motherships. A Deploy command sets up a TaskMap entry - a TaskInfo_t object which contains the pertinent information related to the state, hardware resources, and mappings of this task. - From HostLink, Motherships support communications between Tinsels and + From HostLink, Motherships support communications between Tinsels and the rest of the MPI universe, along with the low-level configuration and command routines. The inheritance relation emphasises that a Mothership *is* a HostLink: without this interface it is meaningless, whilst a Mothership - on a given Box is by definition the one and only HostLink on the Box. + on a given Box is by definition the one and only HostLink on the Box. A separate thread: Twig, extends the HostLink functionality onto the MPI network, providing communications *from* tinsels *to* MPI and also via a private channel to Supervisors. A Supervisor is a bit of application-specific @@ -55,7 +55,7 @@ class Mothership : public SBase, public HostLink virtual ~ Mothership(); unsigned Connect(string=""); - + // somewhat bodgey function to return the hardware address from the composite; needed because of // the actual hardware mappings; this is the Orchestrator-side equivalent of toAddr. static inline unsigned GetHWAddr(P_addr& VAddr) {return (VAddr.A_box << P_BOX_HWOS) | @@ -93,7 +93,7 @@ unsigned OnSyst (PMsg_p *,unsigned); unsigned OnTinsel (PMsg_p*, unsigned); unsigned OnTinselOut (P_Sup_Msg_t *); void StopTwig(); -// forwarded messages containing POETS packets. The Mothership +// forwarded messages containing POETS packets. The Mothership // should unpack the packets contained and inject them using // the HostLink. (This could be placed in a Branch thread). unsigned SendAttr (PMsg_p *, unsigned); @@ -110,11 +110,11 @@ unsigned SystTopo(); void* SuperHandle; // dynamically loadable supervisor int (*SupervisorCall)(PMsg_p*, PMsg_p*); // entry point for the Supervisor - + public: unsigned PAddress; // address of this mothership in POETS-space bool ForwardMsgs; - + typedef unsigned (Mothership::*pMeth)(PMsg_p *,unsigned); typedef map FnMap_t; typedef map PinBuf_t; // type to hold buffers for messages received from devices @@ -128,13 +128,9 @@ pthread_t Twig_thread; // thread for processing tinsel co bool twig_running; // flag to show twig thread is active static const int NumBoards = NUM_BOARDS_PER_BOX; - + }; //============================================================================== -#endif - - - - +#endif \ No newline at end of file diff --git a/Source/OrchBase/OrchBaseLink.cpp b/Source/OrchBase/OrchBaseLink.cpp index b3d23b19..8744603e 100644 --- a/Source/OrchBase/OrchBaseLink.cpp +++ b/Source/OrchBase/OrchBaseLink.cpp @@ -178,7 +178,6 @@ if (!pPlace->Place(pT)) vector supervisorRanks; string supervisorType = pT->pSup->pP_devtyp->Name(); vector supervisorAttrs; - // vector supervisorDevices; vector >::iterator currSuperRank = superRanks.begin(); deviceData.Src(Urank); deviceData.Tgt(nsRank); @@ -190,48 +189,37 @@ if (!pPlace->Place(pT)) while (((*superIter) != pT->pSup) && (++superIter != box->second->P_superv.end())); if (superIter == box->second->P_superv.end()) { - Post(164,pT->Name()); - return; + Post(168,pT->Name()); + pT->LinkFlag(); // have to set the task as linked in order + Unlink(pT->Name()); // to unlink it, which needs to be done to clear + return; // the thread device list. } if (!((*superIter)->pP_devtyp)) { - Post(165,(*superIter)->Name()); + Post(169,(*superIter)->Name()); + pT->LinkFlag(); + Unlink(pT->Name()); return; } - // RecordData_t supervisorRecord; P_addr sAddr = (*superIter)->addr; supervisorNames.push_back((*superIter)->Name()); // temporarily addresses must use old format. Later this should // be changed to the definitive reference standard format. - //supervisorRecord.Address = (sAddr.A_box << P_BOX_OS) | - // (sAddr.A_board << P_BOARD_OS) | - // (sAddr.A_core << P_CORE_OS) | - // (sAddr.A_thread << P_THREAD_OS) | - // (sAddr.A_device << P_DEVICE_OS); supervisorAddrs.push_back((sAddr.A_box << P_BOX_OS) | (sAddr.A_board << P_BOARD_OS) | (sAddr.A_mailbox << P_MAILBOX_OS) | (sAddr.A_core << P_CORE_OS) | (sAddr.A_thread << P_THREAD_OS) | (sAddr.A_device << P_DEVICE_OS)); - //supervisorRecord.DeviceType = sDevType; - //supervisorRecord.RecordType = Supervisor; supervisorAttrs.push_back(0); - // Temporarily, for testing without Motherships, we will give the Supervisor + // For testing without Motherships, we will give the Supervisor // a 'bad' rank. When running with a system that includes Motherships the - // commented code below should be selected. + // commented code below can be selected. if (currSuperRank == superRanks.end()) supervisorRanks.push_back(0xFFFFFFFF); - // supervisorRecord.Rank = 0xFFFFFFFF; // when record bundled as a RecordData_t /* { Post(709,(*superIter)->Name(),box->first); } - else - { - supervisorRecord.Rank = currSuperRank->second; - ++currSuperRank; - supervisorDevices.push_back(supervisorRecord); - } */ else { @@ -239,10 +227,8 @@ if (!pPlace->Place(pT)) // later, this should be fixed. // TODO: extend the rank in a Record_t to include the comm. supervisorRanks.push_back(currSuperRank->second); - // supervisorRecord.Rank = currSuperRank->second; ++currSuperRank; } - // supervisorDevices.push_back(supervisorRecord); // and then find all the 'normal' devices WALKVECTOR(P_board*,box->second->P_boardv,board) { diff --git a/Source/OrchestratorMessages.txt b/Source/OrchestratorMessages.txt index edefdd80..e51cbb9b 100644 --- a/Source/OrchestratorMessages.txt +++ b/Source/OrchestratorMessages.txt @@ -76,6 +76,8 @@ 165(E) : "System command '%s' ran unsuccessfully. Aborting deployment." 166(E) : "Not enough mothership processes exist to deploy task %s. Is a mothership running?" 167(E) : "System command '%s' ran unsuccessfully with message '%s'. Aborting deployment." +168(E) : "Task %s's supervisor does not exist in the box supervisor list" +169(E) : "Supervisor %s does not have a device type - cannot configure NameServer" 508(U) : "Task %s in an unrecognised state. Reboot the mothership" 509(W) : "Task %s could not be loaded because task %s was already active in state %s" From 008048d411b28714b8d1b037ce12213bd08d49ea Mon Sep 17 00:00:00 2001 From: AlexRast Date: Wed, 2 Oct 2019 01:16:40 +0100 Subject: [PATCH 12/14] Ns_el removed, link /nser command temporarily disabled as a consequence, a few small cosmetic changes --- Build/gcc/Makefile.executable_prerequisites | 1 - OrchestratorFrontEnd.pro | 2 - Source/Mothership/MothershipDummy.cpp | 14 +- Source/NameServer/NameServer.cpp | 1 - Source/NameServer/NameServer.h | 1 - Source/NameServer/Ns_el.cpp | 481 -------------------- Source/NameServer/Ns_el.h | 169 ------- Source/NameServer/SBase.cpp | 2 +- Source/OrchBase/OrchBase.cpp | 2 +- Source/OrchBase/OrchBase.h | 3 +- Source/OrchBase/OrchBaseLink.cpp | 6 +- 11 files changed, 14 insertions(+), 668 deletions(-) delete mode 100644 Source/NameServer/Ns_el.cpp delete mode 100644 Source/NameServer/Ns_el.h diff --git a/Build/gcc/Makefile.executable_prerequisites b/Build/gcc/Makefile.executable_prerequisites index ef269af9..6dc7cd3e 100755 --- a/Build/gcc/Makefile.executable_prerequisites +++ b/Build/gcc/Makefile.executable_prerequisites @@ -135,7 +135,6 @@ ROOT_SOURCES = $(SOURCE_DIR)/Root/RootMain.cpp \ $(GENERICS_DIR)/lex.cpp \ $(GENERICS_DIR)/rand.cpp \ $(SOURCE_DIR)/Common/Debug.cpp \ - $(SOURCE_DIR)/NameServer/Ns_el.cpp \ $(SOURCE_DIR)/NameServer/ABDefs.cpp \ $(SOURCE_DIR)/NameServer/ABRecord.cpp \ $(SOURCE_DIR)/NameServer/ABTask.cpp \ diff --git a/OrchestratorFrontEnd.pro b/OrchestratorFrontEnd.pro index 133bf129..7b9ee83b 100644 --- a/OrchestratorFrontEnd.pro +++ b/OrchestratorFrontEnd.pro @@ -88,7 +88,6 @@ SOURCES += \ ./Generics/uif.cpp \ Source/Common/Pglobals.cpp \ Source/OrchBase/P_super.cpp \ - Source/NameServer/Ns_el.cpp \ Source/OrchBase/P_owner.cpp \ Source/OrchBase/build_defs.cpp \ Source/OrchBase/CMsg_p.cpp @@ -181,7 +180,6 @@ HEADERS += \ ./Source/OrchBase/build_defs.h \ Source/OrchBase/P_super.h \ Source/Injector/Injector.h \ - Source/NameServer/Ns_el.h \ Source/OrchBase/P_owner.h \ Generics/dumpchan.h \ Source/OrchBase/CMsg_p.h \ diff --git a/Source/Mothership/MothershipDummy.cpp b/Source/Mothership/MothershipDummy.cpp index 610a6b5c..a4ceed57 100644 --- a/Source/Mothership/MothershipDummy.cpp +++ b/Source/Mothership/MothershipDummy.cpp @@ -589,7 +589,7 @@ unsigned MothershipDummy::ReplyAttrs(PMsg_p *msg, unsigned comm) } fprintf(dumpFile, "--------------------- Devices By Attribute -------------------------------------\n"); for (unsigned dev = 0; dev < numDevs; dev++) - fprintf(dumpFile, "Device %s: address 0x%llx\n", devNames[dev].c_str(), devAddrs[dev]); + fprintf(dumpFile, "Device %s: address 0x%lx\n", devNames[dev].c_str(), devAddrs[dev]); fprintf(dumpFile, "\n"); fprintf(dumpFile, "Matched attributes:\n"); for (unsigned attr = 0; attr < devAttrs.size(); attr++) @@ -619,7 +619,7 @@ unsigned MothershipDummy::ReplyDevice(PMsg_p *msg, unsigned comm) } string qryType = QueryType(msg->Key()); fprintf(dumpFile, "--------------------- %s ----------------------------\n", qryType.c_str()); - fprintf(dumpFile, "Device %s: address 0x%llx\n", msg->Zname(1).c_str(), *devAddr); + fprintf(dumpFile, "Device %s: address 0x%lx\n", msg->Zname(1).c_str(), *devAddr); fprintf(dumpFile, "________________________________________________________________________________\n"); fclose(dumpFile); qryMap.erase(qryTag); // Reply handled; remove the query request @@ -654,7 +654,7 @@ unsigned MothershipDummy::ReplyDevices(PMsg_p *msg, unsigned comm) fprintf(dumpFile, "----------- %s ----------------------------\n", qryType.c_str()); fprintf(dumpFile, "Matching device %s\n", devName.c_str()); for (unsigned dev = 0; dev < numDevs; dev++) - fprintf(dumpFile, "Device %s: address 0x%llx\n", devNames[dev].c_str(), devAddrs[dev]); + fprintf(dumpFile, "Device %s: address 0x%lx\n", devNames[dev].c_str(), devAddrs[dev]); fprintf(dumpFile, "________________________________________________________________________________\n"); fclose(dumpFile); qryMap.erase(qryTag); // Reply handled; remove the query request @@ -686,7 +686,7 @@ unsigned MothershipDummy::ReplyDevSuper(PMsg_p *msg, unsigned comm) } string qryType = QueryType(msg->Key()); fprintf(dumpFile, "--------------------- %s ----------------------------\n", qryType.c_str()); - fprintf(dumpFile, "Device %s: address 0x%llx, supervisor 0x%llx\n", msg->Zname(1).c_str(),*devAddr,*superAddr); + fprintf(dumpFile, "Device %s: address 0x%lx, supervisor 0x%lx\n", msg->Zname(1).c_str(),*devAddr,*superAddr); fprintf(dumpFile, "________________________________________________________________________________\n"); fclose(dumpFile); qryMap.erase(qryTag); // Reply handled; remove the query request @@ -722,7 +722,7 @@ unsigned MothershipDummy::ReplyDevTypes(PMsg_p *msg, unsigned comm) fprintf(dumpFile, "----------- %s ----------------------------\n", qryType.c_str()); fprintf(dumpFile, "Matching %s type %s\n", matchType.c_str(), matchName.c_str()); for (unsigned dev = 0; dev < numDevs; dev++) - fprintf(dumpFile, "Device %s: address 0x%llx\n", devNames[dev].c_str(), devAddrs[dev]); + fprintf(dumpFile, "Device %s: address 0x%lx\n", devNames[dev].c_str(), devAddrs[dev]); fprintf(dumpFile, "________________________________________________________________________________\n"); fclose(dumpFile); qryMap.erase(qryTag); // Reply handled; remove the query request @@ -794,7 +794,7 @@ unsigned MothershipDummy::ReplySupers(PMsg_p *msg, unsigned comm) } fprintf(dumpFile, "--------------------- Supervisors -------------------------------------\n"); for (unsigned dev = 0; dev < numDevs; dev++) - fprintf(dumpFile, "Supervisor %s: address 0x%llx, rank %d\n",supNames[dev].c_str(),supAddrs[dev],supRanks[dev]); + fprintf(dumpFile, "Supervisor %s: address 0x%lx, rank %lu\n",supNames[dev].c_str(),supAddrs[dev],supRanks[dev]); fprintf(dumpFile, "________________________________________________________________________________\n"); fclose(dumpFile); qryMap.erase(qryTag); // Reply handled; remove the query request @@ -823,7 +823,7 @@ unsigned MothershipDummy::ReplyTask(PMsg_p *msg, unsigned comm) msg->Get(2, counts); if (counts.size() != 3) { - fprintf(dumpFile, "ERROR: count mismatch for task info: expected 3 counts, received %u\n", counts.size()); + fprintf(dumpFile, "ERROR: count mismatch for task info: expected 3 counts, received %lu\n", counts.size()); return ERR_NONFATAL; } fprintf(dumpFile, "-------------------------------- Task Info -------------------------------------\n"); diff --git a/Source/NameServer/NameServer.cpp b/Source/NameServer/NameServer.cpp index 038d679b..c905af57 100644 --- a/Source/NameServer/NameServer.cpp +++ b/Source/NameServer/NameServer.cpp @@ -4,7 +4,6 @@ #include "PMsg_p.hpp" #include "mpi.h" #include "Pglobals.h" -// #include "Ns_el.h" // #include "jnj.h" #include "CMsg_p.h" #include diff --git a/Source/NameServer/NameServer.h b/Source/NameServer/NameServer.h index aa877438..0065a68d 100644 --- a/Source/NameServer/NameServer.h +++ b/Source/NameServer/NameServer.h @@ -3,7 +3,6 @@ #include "SBase.h" #include "Debug.h" -// #include "Ns_el.h" //============================================================================== diff --git a/Source/NameServer/Ns_el.cpp b/Source/NameServer/Ns_el.cpp deleted file mode 100644 index dff9678d..00000000 --- a/Source/NameServer/Ns_el.cpp +++ /dev/null @@ -1,481 +0,0 @@ -//------------------------------------------------------------------------------ - -#include "Ns_el.h" - -//============================================================================== - -Ns_el::Ns_el() -{ -Put(); -Put(); -Put(); -Put(); -} - -//------------------------------------------------------------------------------ - -Ns_el::Ns_el(byte * s,int l):PMsg_p(s,l) -{ -Put(); -Put(); -Put(); -Put(); -} - -//------------------------------------------------------------------------------ - -Ns_el::Ns_el(PMsg_p * p):PMsg_p(*p) -{ -Put(); -Put(); -Put(); -Put(); -Dump(); -} - -//------------------------------------------------------------------------------ - -Ns_el::~Ns_el() -{ -} - -//------------------------------------------------------------------------------ - -vector * Ns_el::Construct() -// Receiver side (NameServer) function: -// Routine to build and expose a vector of entities derived from the current -// state of the object. -// If there's a problem - and there shouldn't be - a entity type 'X' is written -// to the output vector and the routine returns prematurely (i.e. the 'X' entity -// is the last in the returned vector). Subsequent parts of the PMsg_p are -// ignored. -{ -Ns_X * pX = new Ns_X(); // Error entity -static vector vN; -vN.clear(); // Output entity vector -int cnt; // Stored key vector size -unsigned * ploc = Get(0,cnt);// Get hold of the key vector -try { - if (ploc==0) throw((*pX)("KEY VECTOR")); // If it's not there.... - for (int i=0;i(*ploc,c2); - if (pEtype==0) throw((*pX)("ENTITY TYPE")); - vector vs; // Holder for the packed name vector - Ns_0 * p; // Entity - GetX(*ploc,vs); // Slurp it out - switch (*pEtype) { - case 'D' : { Ns_el::Dindex_t * pDindex = Get(*ploc,c2); - if (pDindex==0) throw((*pX)("DEVICE INDEX")); - p = new Ns_dev(pDindex,vs); // No idea why - - vN.push_back(p); // Another BORLAND bug? - } break; - case 'S' : { Ns_el::Sindex_t * pSindex = Get(*ploc,c2); - if (pSindex==0) throw((*pX)("SUPERVISOR INDEX")); - p = new Ns_sup(pSindex,vs); // No idea why - - vN.push_back(p); // Another BORLAND bug? - } break; - case 'T' : { Ns_el::Tindex_t * pTindex = Get(*ploc,c2); - if (pTindex==0) throw((*pX)("TASK INDEX")); - p = new Ns_tsk(pTindex,vs); // No idea why - - vN.push_back(p); // Another BORLAND bug? - } break; - case 'O' : { Ns_el::Oindex_t * pOindex = Get(*ploc,c2); - if (pOindex==0) throw((*pX)("OWNER INDEX")); - p = new Ns_own(pOindex,vs); // No idea why - - vN.push_back(p); // Another BORLAND bug? - } break; - default : throw((*pX)("UNRECOGNISED ENTITY TYPE")); - } // switch - p->type = *pEtype; - p->key = *ploc; - } // for -} // try -catch(Ns_0 * X) { // Cockup: Push the already allocated - vN.push_back(X); // error entity onto the vector and bail - return &vN; -} -delete pX; // Don't need the error entity -return &vN; -} - -//------------------------------------------------------------------------------ - -void Ns_el::Dump(FILE * fp) -// The usual, but the logic is a bit fiddly here. We want to dump the *contents* -// of the PMsg_p class that is the base class of Ns_el. Calling PMsg_p::Dump() -// will not do the biz, because PMsg_p doesn't know anything about types, so the -// Dump() will be largely impenetrable. The only state variable of Ns_el is -// keyv, the vector of keys that have currently been used to load the class. -// So we walk keyv, extracting the (integer) keys. We can use these to get the -// entity types: Get(keyv[?],...), and this gives us the complete -// signature for the entity: {key,type}. -// If keyv is empty, this is either because the object is genuinely empty, -// or because the POETS entities have been streamed to the internal byte -// buffer. -{ -fprintf(fp,"Ns_el+++++++++++++++++++++++++++++++++++++\n"); -fprintf(fp,"Current key vector (%zu elements):\n",keyv.size()); -WALKVECTOR(unsigned,keyv,i)fprintf(fp,"%3u\n",*i); -fprintf(fp,"+ + + + + + + + + + + + + + + + + + + + + + + + + +\n"); -fprintf(fp,"Walking the entities; dumping with unstreamed keys:\n"); - // Dump with the unstreamed keys -WALKVECTOR(unsigned,keyv,i) Dump0(fp,*i); -fprintf(fp,"+ + + + + + + + + + + + + + + + + + + + + + + + + +\n"); -fprintf(fp,"Walking the entities; dumping with stored key vector:\n"); -int cnt; // Stored key vector size -unsigned * ploc = Get(0,cnt);// Get hold of the key vector -fprintf(fp,"Stored key vector has %u elements\n",cnt); - // Dump with streamed keys -if (ploc!=0) for(int i=0;i(k,cnt); -unsigned char Etype = 'X'; -if (pEtype==0) fprintf(fp,"*** TYPE UNLOAD FAIL ***\n"); -else Etype = *pEtype; -vector vs; -switch (Etype) { - case 'D' : { fprintf(fp,"\nDevice key %3u:\n",k); - Ns_el::Dindex_t Dindex; - Ns_el::Dindex_t * pDindex = Get(k,cnt); - if (pDindex==0) { - fprintf(fp,"*** DEVICE INDEX UNLOAD FAIL ***\n"); - break; - } - else Dindex = *pDindex; - fprintf(fp,"key = %u\n",Dindex.key); - if (Dindex.key!=k) fprintf(fp,"*** KEY MISMATCH ***\n"); - Dindex.addr.Dump(fp); - fprintf(fp,"attr = %u\n",Dindex.attr); - fprintf(fp,"bin = %u\n",Dindex.bin); - fprintf(fp,"inP(size) = %u\n",Dindex.inP/2); - fprintf(fp,"ouP(size) = %u\n",Dindex.ouP/2); - GetX(k,vs); - fprintf(fp,"Name vector (%zu elements)\n",vs.size()); - WALKVECTOR(string,vs,i) fprintf(fp,"%s\n",(*i).c_str()); - } break; - case 'S' : { fprintf(fp,"\nSupervisor key %3u:\n",k); - Ns_el::Sindex_t Sindex; - Ns_el::Sindex_t * pSindex = Get(k,cnt); - if (pSindex==0) { - fprintf(fp,"*** SUPERVISOR INDEX UNLOAD FAIL ***\n"); - break; - } - else Sindex = *pSindex; - fprintf(fp,"key = %u\n",Sindex.key); - if (Sindex.key!=k) fprintf(fp,"*** KEY MISMATCH ***\n"); - Sindex.addr.Dump(fp); - fprintf(fp,"attr = %u\n",Sindex.attr); - fprintf(fp,"bin = %u\n",Sindex.bin); - GetX(k,vs); - fprintf(fp,"Name vector (%zu elements)\n",vs.size()); - WALKVECTOR(string,vs,i) fprintf(fp,"%s\n",(*i).c_str()); - } break; - case 'T' : { fprintf(fp,"\nTask key %3u:\n",k); - Ns_el::Tindex_t Tindex; - Ns_el::Tindex_t * pTindex = Get(k,cnt); - if (pTindex==0) { - fprintf(fp,"*** TASK INDEX UNLOAD FAIL ***\n"); - break; - } - else Tindex = *pTindex; - fprintf(fp,"key = %u\n",Tindex.key); - if (Tindex.key!=k) fprintf(fp,"*** KEY MISMATCH ***\n"); - GetX(k,vs); - fprintf(fp,"Name vector (%zu elements)\n",vs.size()); - WALKVECTOR(string,vs,i) fprintf(fp,"%s\n",(*i).c_str()); - } break; - - case 'O' : { fprintf(fp,"\nOwner key %3u:\n",k); - Ns_el::Oindex_t Oindex; - Ns_el::Oindex_t * pOindex = Get(k,cnt); - if (pOindex==0) { - fprintf(fp,"*** OWNER INDEX UNLOAD FAIL ***\n"); - break; - } - else Oindex = *pOindex; - fprintf(fp,"key = %u\n",Oindex.key); - if (Oindex.key!=k) fprintf(fp,"*** KEY MISMATCH ***\n"); - GetX(k,vs); - fprintf(fp,"Name vector (%zu elements)\n",vs.size()); - WALKVECTOR(string,vs,i) fprintf(fp,"%s\n",(*i).c_str()); - } break; - default : fprintf(fp,"*** UNRECOGNISED ENTITY TYPE ***\n");; -} - -} - -//------------------------------------------------------------------------------ - -int Ns_el::Length() -// Retrieve length of streamed message -{ -if (!keyv.empty()) { - Put(0,&keyv[0],keyv.size()); // Load the key list - keyv.clear(); // Everything is in the stream buffer -} -Msg_p::Stream(); -return (int)Msg_p::vm.size(); -} - -//------------------------------------------------------------------------------ - -void Ns_el::PutD(string Dname, // Device name - string Dtype, // Device type - string Sname, // Supervisor name - string Tname, // Task name - string Oname, // Owner name - vector inpin, // Device input pin names - vector inpintype, // Device input pin types - vector oupin, // Device output pin names - vector oupintype, // Device output pin types - unsigned key, // PMsg_p key - P_addr_t addr, // POETS hardware address - unsigned attr, // Device attribute - unsigned bin) // Binary file indentifier -{ -struct Ns_el::Dindex_t Dindex; -// This rather clunky initialisation section is the highest common factor of my -// three C++ compilers..... -Dindex.key = key; // PMsg_p key -Dindex.addr = addr; // POETS composite address -Dindex.attr = attr; // POETS device attribute -Dindex.bin = bin; // ..and so on: see above -Dindex.inP = inpin.size() + inpintype.size(); -Dindex.ouP = oupin.size() + oupintype.size(); -vector vs; // Name container -vs.push_back(Dname); // Device name -vs.push_back(Dtype); // Device type -vs.push_back(Sname); // Supervisor name -vs.push_back(Tname); // Task name -vs.push_back(Oname); // Owner name -vs.insert(vs.end(),inpin.begin(),inpin.end()); -vs.insert(vs.end(),inpintype.begin(),inpintype.end()); -vs.insert(vs.end(),oupin.begin(),oupin.end()); -vs.insert(vs.end(),oupintype.begin(),oupintype.end()); -unsigned char Entity = 'D'; -Put(key,&Entity); // Entity type (device/supervisor...) -Put(key,&Dindex); // Index structure -PutX(key,&vs); // All the strings in one container -keyv.push_back(key); // Add to the vector of used keys -} - -//------------------------------------------------------------------------------ - -void Ns_el::PutO(string Oname, // Owner name - unsigned key) // PMsg_p key -{ -struct Ns_el::Oindex_t Oindex; -// This rather clunky initialisation section is the highest common factor of my -// three C++ compilers..... -Oindex.key = key; // PMsg_p key -vector vs; // Name container -vs.push_back(Oname); // Owner name -unsigned char Entity = 'O'; -Put(key,&Entity); // Entity type (device/supervisor...) -Put(key,&Oindex); // Index structure -PutX(key,&vs); // All the strings in one container -keyv.push_back(key); // Add to the vector of used keys -} - -//------------------------------------------------------------------------------ - -void Ns_el::PutS(string Sname, // Supervisor name - string Tname, // Task name - string Oname, // Owner name - unsigned key, // PMsg_p key - P_addr_t addr, // POETS hardware address - unsigned attr, // Device attribute - unsigned bin) // Binary file indentifier -{ -struct Ns_el::Sindex_t Sindex; -// This rather clunky initialisation section is the highest common factor of my -// three C++ compilers..... -Sindex.key = key; // PMsg_p key -Sindex.addr = addr; // POETS composite address -Sindex.attr = attr; // POETS device attribute -Sindex.bin = bin; // ..and so on: see above -vector vs; // Name container -vs.push_back(Sname); // Supervisor name -vs.push_back(Tname); // Task name -vs.push_back(Oname); // Owner name -unsigned char Entity = 'S'; -Put(key,&Entity); // Entity type (device/supervisor...) -Put(key,&Sindex); // Index structure -PutX(key,&vs); // All the strings in one container -keyv.push_back(key); // Add to the vector of used keys -} - -//------------------------------------------------------------------------------ - -void Ns_el::PutT(string Tname, // Task name - string Oname, // Owner name - unsigned key) // PMsg_p key -{ -struct Ns_el::Tindex_t Tindex; -// This rather clunky initialisation section is the highest common factor of my -// three C++ compilers..... -Tindex.key = key; // PMsg_p key -vector vs; // Name container -vs.push_back(Tname); // Task name -vs.push_back(Oname); // Owner name -unsigned char Entity = 'T'; // Guess -Put(key,&Entity); // Entity type (device/supervisor...) -Put(key,&Tindex); // Index structure -PutX(key,&vs); // All the strings in one container -keyv.push_back(key); // Add to the vector of used keys -} - -//============================================================================== - -Ns_X * Ns_X::operator()(string s) -{ -msg = s; -return this; -} - -//============================================================================== - -Ns_dev::Ns_dev(Ns_el::Dindex_t * pDindex,vector & vs) -{ -addr = pDindex->addr; -attr = pDindex->attr; -bin = pDindex->bin; -Ename = vs[0]; -Etype = vs[1]; -Sname = vs[2]; -Tname = vs[3]; -Oname = vs[4]; -vector::iterator inbegin = vs.begin()+5; -vector::iterator inend = inbegin + pDindex->inP/2; -vector::iterator intypebegin = inend; -vector::iterator intypeend = intypebegin + pDindex->inP/2; -vector::iterator oubegin = intypeend; -vector::iterator ouend = oubegin + pDindex->ouP/2; -vector::iterator outypebegin = ouend; -vector::iterator outypeend = outypebegin + pDindex->ouP/2; -inpin = vector(inbegin,inend); -inpintype = vector(intypebegin,intypeend); -oupin = vector(oubegin,ouend); -oupintype = vector(outypebegin,outypeend); -} - -//------------------------------------------------------------------------------ - -void Ns_dev::Dump(FILE * fp) -{ -fprintf(fp,"Ns_dev++++++++++++++++++++++++++++++++++++\n"); -addr.Dump(fp); -fprintf(fp,"Device type = %s\n",Etype.c_str()); -fprintf(fp,"Super name = %s\n",Sname.c_str()); -fprintf(fp,"Task name = %s\n",Tname.c_str()); -fprintf(fp,"Owner name = %s\n",Oname.c_str()); -fprintf(fp,"attr = %u\n",attr); -fprintf(fp,"bin = %u\n",bin); -fprintf(fp,"\nInput pin name vector (%zu elements)\n",inpin.size()); -WALKVECTOR(string,inpin,i) fprintf(fp,"%s\n",(*i).c_str()); -fprintf(fp,"\nInput pin type name vector (%zu elements)\n",inpintype.size()); -WALKVECTOR(string,inpintype,i) fprintf(fp,"%s\n",(*i).c_str()); -fprintf(fp,"\nOutput pin name vector (%zu elements)\n",oupin.size()); -WALKVECTOR(string,oupin,i) fprintf(fp,"%s\n",(*i).c_str()); -fprintf(fp,"\nOutput pin type name vector (%zu elements)\n",oupintype.size()); -WALKVECTOR(string,oupintype,i) fprintf(fp,"%s\n",(*i).c_str()); -Ns_0::Dump(fp); -fprintf(fp,"Ns_dev------------------------------------\n"); -} - -//============================================================================== - -Ns_0::Ns_0() -{ -} - -//------------------------------------------------------------------------------ - -void Ns_0::Dump(FILE * fp) -{ -fprintf(fp,"Ns_0++++++++++++++++++++++++++++++++++++++\n"); -fprintf(fp,"key = %u\n",key); -fprintf(fp,"type = %c\n",type); -fprintf(fp,"entity name = %s\n",Ename.c_str()); -fprintf(fp,"Ns_0--------------------------------------\n"); -} - -//============================================================================== - -Ns_sup::Ns_sup(Ns_el::Sindex_t * pSindex,vector & vs) -{ -Ename = vs[0]; -Tname = vs[1]; -Oname = vs[2]; -addr = pSindex->addr; -attr = pSindex->attr; -bin = pSindex->bin; -} - -//------------------------------------------------------------------------------ - -void Ns_sup::Dump(FILE * fp) -{ -fprintf(fp,"Ns_sup++++++++++++++++++++++++++++++++++++\n"); -addr.Dump(fp); -fprintf(fp,"Task name = %s\n",Tname.c_str()); -fprintf(fp,"Owner name = %s\n",Oname.c_str()); -fprintf(fp,"attr = %u\n",attr); -fprintf(fp,"bin = %u\n",bin); -Ns_0::Dump(fp); -fprintf(fp,"Ns_sup------------------------------------\n"); -} - -//============================================================================== - -Ns_tsk::Ns_tsk(Ns_el::Tindex_t *,vector & vs) -{ -Ename = vs[0]; -Oname = vs[1]; -} - -//------------------------------------------------------------------------------ - -void Ns_tsk::Dump(FILE * fp) -{ -fprintf(fp,"Ns_tsk++++++++++++++++++++++++++++++++++++\n"); -fprintf(fp,"Owner name = %s\n",Oname.c_str()); -Ns_0::Dump(fp); -fprintf(fp,"Ns_tsk------------------------------------\n"); -} - -//============================================================================== - -Ns_own::Ns_own(Ns_el::Oindex_t *,vector & vs) -{ -Ename = vs[0]; -} - -//------------------------------------------------------------------------------ - -void Ns_own::Dump(FILE * fp) -{ -fprintf(fp,"Ns_own++++++++++++++++++++++++++++++++++++\n"); -Ns_0::Dump(fp); -fprintf(fp,"Ns_own------------------------------------\n"); -} - -//============================================================================== - diff --git a/Source/NameServer/Ns_el.h b/Source/NameServer/Ns_el.h deleted file mode 100644 index daed295a..00000000 --- a/Source/NameServer/Ns_el.h +++ /dev/null @@ -1,169 +0,0 @@ -#ifndef __Ns_elH__H -#define __Ns_elH__H - -#include "PMsg_p.hpp" -#include "P_addr.h" -#include "NameBase.h" -#include -#include -using namespace std; - -//============================================================================== - -class Ns_0; -class Ns_el : public PMsg_p -{ -public: - Ns_el(); - Ns_el(byte *,int); - Ns_el(PMsg_p *); -virtual ~ Ns_el(); - -vector * Construct(); -void Dump(FILE * = stdout); -void Dump0(FILE *,unsigned); -int Length(); -void PutD(string,string,string,string,string,vector, - vector,vector,vector,unsigned, - P_addr_t,unsigned,unsigned); -void PutO(string,unsigned); -void PutS(string,string,string,unsigned,P_addr_t,unsigned,unsigned); -void PutT(string,string,unsigned); - -struct Dindex_t { - unsigned key; - P_addr_t addr; - unsigned attr; - unsigned bin; - unsigned inP; - unsigned ouP; -}; -struct Oindex_t { - unsigned key; -}; -struct Sindex_t { - unsigned key; - P_addr_t addr; - unsigned attr; - unsigned bin; -}; -struct Tindex_t { - unsigned key; -}; - -vector keyv; - -}; - -//============================================================================== - -class Ns_0 : public NameBase -{ -public: - Ns_0(); -virtual ~ Ns_0(){} - -virtual void Dump(FILE * = stdout); - -unsigned key; -unsigned char type; -string Ename; - -}; - -//============================================================================== - -class Ns_X : public Ns_0 -{ -public: - Ns_X(string = ""){} -Ns_X * operator()(string); -virtual ~ Ns_X(){} - -private: -string msg; - -protected: -virtual void Dump(FILE * = stdout){} -}; - -//============================================================================== - -class Ns_dev : public Ns_0 -{ -public: - Ns_dev(Ns_el::Dindex_t *,vector &); -virtual ~ Ns_dev(){} - -protected: -virtual void Dump(FILE * = stdout); - -private: -string Etype; -string Sname; -string Tname; -string Oname; -P_addr_t addr; -unsigned attr; -unsigned bin; -vector inpin; -vector inpintype; -vector oupin; -vector oupintype; -}; - -//============================================================================== - -class Ns_sup : public Ns_0 -{ -public: - Ns_sup(Ns_el::Sindex_t *,vector &); -virtual ~ Ns_sup(){} - -protected: -virtual void Dump(FILE * = stdout); - -private: -string Tname; -string Oname; -P_addr_t addr; -unsigned attr; -unsigned bin; - -}; - -//============================================================================== - -class Ns_tsk : public Ns_0 -{ -public: - Ns_tsk(Ns_el::Tindex_t *,vector &); -virtual ~ Ns_tsk(){} - -protected: -virtual void Dump(FILE * = stdout); - -private: -string Oname; - -}; - -//============================================================================== - -class Ns_own : public Ns_0 -{ -public: - Ns_own(Ns_el::Oindex_t *,vector &); -virtual ~ Ns_own(){} - -protected: -virtual void Dump(FILE * = stdout); -}; - -//============================================================================== - -#endif - - - - diff --git a/Source/NameServer/SBase.cpp b/Source/NameServer/SBase.cpp index b9af493d..9d2094e6 100644 --- a/Source/NameServer/SBase.cpp +++ b/Source/NameServer/SBase.cpp @@ -628,7 +628,7 @@ unsigned SBase::DataDevType(PMsg_p *msg) /* DataDevice - Load this task with Device info. This is the main function to populate the database. Device records may be sent in several such messages, - although it is standard to send them all in one messaeg. + although it is standard to send them all in one message. */ //============================================================================== unsigned SBase::DataDevice(PMsg_p *msg) diff --git a/Source/OrchBase/OrchBase.cpp b/Source/OrchBase/OrchBase.cpp index 0958f38d..2a32ae16 100644 --- a/Source/OrchBase/OrchBase.cpp +++ b/Source/OrchBase/OrchBase.cpp @@ -14,7 +14,7 @@ #include "SimpleDeployer.h" #include "Dialect1Deployer.h" #include "MultiSimpleDeployer.h" -#include "Ns_el.h" +// #include "Ns_el.h" #include "P_super.h" #include "poets_msg.h" // brings in constants to set hardware addresses. This should be rationalised. #include "ABDefs.hpp" // TODO: integrate 11 July 2019 ADR diff --git a/Source/OrchBase/OrchBase.h b/Source/OrchBase/OrchBase.h index e01b89a6..8dac0e07 100644 --- a/Source/OrchBase/OrchBase.h +++ b/Source/OrchBase/OrchBase.h @@ -18,6 +18,7 @@ class Dialect1Deployer; #include "Environment.h" #include "CommonBase.h" #include "P_owner.h" +// include "Ns_el.h" using namespace std; #define TASK_DEPLOY_DIR ".orchestrator/task_binaries" // Relative to home. @@ -72,7 +73,7 @@ unsigned CmLink(Cli *); void LinkCons(Cli::Cl_t); void LinkDump(Cli::Cl_t); void LinkLink(Cli::Cl_t); -void LinkNser(Cli::Cl_t); +// void LinkNser(Cli::Cl_t); void LinkPath(Cli::Cl_t); void LinkPlac(Cli::Cl_t); void LinkUnli(Cli::Cl_t); diff --git a/Source/OrchBase/OrchBaseLink.cpp b/Source/OrchBase/OrchBaseLink.cpp index 8744603e..c07e6d36 100644 --- a/Source/OrchBase/OrchBaseLink.cpp +++ b/Source/OrchBase/OrchBaseLink.cpp @@ -12,7 +12,7 @@ WALKVECTOR(Cli::Cl_t,pC->Cl_v,i) { if (strcmp(cs,"cons")==0) { LinkCons(*i); continue; } if (strcmp(cs,"dump")==0) { LinkDump(*i); continue; } if (strcmp(cs,"link")==0) { LinkLink(*i); continue; } - if (strcmp(cs,"nser")==0) { LinkNser(*i); continue; } + // if (strcmp(cs,"nser")==0) { LinkNser(*i); continue; } if (strcmp(cs,"path")==0) { LinkPath(*i); continue; } if (strcmp(cs,"plac")==0) { LinkPlac(*i); continue; } if (strcmp(cs,"unli")==0) { LinkUnli(*i); continue; } @@ -317,7 +317,7 @@ for(unsigned i=0;i Date: Tue, 15 Oct 2019 16:45:16 +0100 Subject: [PATCH 13/14] Comments in Decode.cpp and bugfix for issue 93 incorporated --- Source/Common/Decode.cpp | 4 ++++ Source/Common/SDecode.cpp | 6 ++++++ Source/OrchBase/P_builder.cpp | 1 + 3 files changed, 11 insertions(+) diff --git a/Source/Common/Decode.cpp b/Source/Common/Decode.cpp index bc0cab0e..835f8606 100644 --- a/Source/Common/Decode.cpp +++ b/Source/Common/Decode.cpp @@ -11,6 +11,10 @@ DON'T PUT ANY #include IN THIS FILE It's virtual here because this makes it an invalid translation unit, so if you try to compile it, the compiler will squeak. + +A similar decode exists in SDecode.cpp, intended specifically for classes that +derive from SBase. Do not use this file (Decode.cpp) if you are deriving from +SBase. Similarly, do not use SDecode.cpp if you derive directly from CommonBase. */ //============================================================================== diff --git a/Source/Common/SDecode.cpp b/Source/Common/SDecode.cpp index d2713fce..4d0a0e15 100644 --- a/Source/Common/SDecode.cpp +++ b/Source/Common/SDecode.cpp @@ -11,6 +11,12 @@ DON'T PUT ANY #include IN THIS FILE It's virtual here because this makes it an invalid translation unit, so if you try to compile it, the compiler will squeak. + +This particular version - SDecode, is intended for use in classes that +derive from SBase. It's duplicated from Decode.cpp because (1) you can't use +a dynamic_cast to check whether the class is derived from SBase without including +all the SBase code anyway in classes that have nothing to do with it; (2) we +don't want to use an #ifdef directive as an ugly 'solution' for this. */ //============================================================================== diff --git a/Source/OrchBase/P_builder.cpp b/Source/OrchBase/P_builder.cpp index cdac4f2e..73ea88e8 100644 --- a/Source/OrchBase/P_builder.cpp +++ b/Source/OrchBase/P_builder.cpp @@ -167,6 +167,7 @@ void P_builder::Preplace(P_task* task) MultiSimpleDeployer deployer(numBoxes); par->Post(138,par->pE->Name()); deployer.deploy(par->pE); + par->pPlace->Init(); } if (!par->pPlace->Place(task)) task->LinkFlag(); // then preplace on the real or virtual board. } From 01d63dd5ceb447876d2850103e1a27da049cd267 Mon Sep 17 00:00:00 2001 From: AlexRast Date: Wed, 16 Oct 2019 16:04:21 +0100 Subject: [PATCH 14/14] updated Mothership to match Matt's new HostLink receive which always grabs an entire flit --- Source/Mothership/Mothership.cpp | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/Source/Mothership/Mothership.cpp b/Source/Mothership/Mothership.cpp index 75eaf857..9a17d47d 100644 --- a/Source/Mothership/Mothership.cpp +++ b/Source/Mothership/Mothership.cpp @@ -1296,7 +1296,6 @@ void* Mothership::Twig(void* par) // 'for later'. { Mothership* parent = static_cast(par); - const uint32_t szFlit = (1<(recv_buf); FILE* OutFile; @@ -1329,9 +1328,6 @@ void* Mothership::Twig(void* par) // if not set up a new buffer for the device if (parent->TwigExtMap[m_hdr->destDeviceAddr] == 0) parent->TwigExtMap[m_hdr->destDeviceAddr] = new deque; - // if the message is multi-flit, get the rest of it now - if (m_hdr->messageLenBytes > szFlit) - parent->recvMsg(recv_buf+szFlit, m_hdr->messageLenBytes-szFlit); parent->TwigExtMap[m_hdr->destDeviceAddr]->push_back(*(static_cast(p_recv_buf))); } else @@ -1374,19 +1370,8 @@ void* Mothership::Twig(void* par) P_Sup_Msg_t* recvdMsg = static_cast(static_cast ((*(parent->TwigMap[s_hdr->sourceDeviceAddr]))[s_hdr->destPin])); - // stuff header into the persistent buffer - memcpy(recvdMsg+s_hdr->seq,s_hdr,p_sup_hdr_size()); - DebugPrint("Expecting message of total length %d\n", - s_hdr->cmdLenBytes); - // more message to receive? - uint32_t len = s_hdr->seq == s_hdr->cmdLenBytes/p_sup_msg_size() ? - s_hdr->cmdLenBytes%p_sup_msg_size() : - p_sup_msg_size(); - DebugPrint("Length for sequence number %d: %d\n", - s_hdr->seq, len); - // get the whole message - if (len > szFlit) parent->recvMsg(((recvdMsg+s_hdr->seq)->data), - len-szFlit); + // stuff message into the persistent buffer + memcpy(recvdMsg+s_hdr->seq,recv_buf,p_sup_msg_size()); if (super_buf_recvd(recvdMsg)) { DebugPrint("Entire Supervisor message received of length %d\n", s_hdr->cmdLenBytes);