Commit 22f5aa04 authored by Sridutt Bhalachandra's avatar Sridutt Bhalachandra Committed by Sridutt Bhalachandra

Initial commit

parents
CC = gcc
CXX = g++
FORT = gfortran
AR = ar
OPT = -O2 -g
LIBS = -L. -lrt
INCLUDE = /home/sriduttb/shared/mpich-install/include
ARFLAGS = rcs
SHARED_LIB_FLAGS = -shared
NRM_LIBS = -Wl,-R /home/sriduttb/shared/utils/zeromq/zeromq-4.1.6/.libs -lzmq
# Processor, system and cpp string for RCRdaemon
RCRPROC = HW
RCRDAEMONCPP = RCRdaemon$(RCRPROC).cpp
RCRDAEMON = RCRdaemon$(RCRPROC)
# Source files for RCRdaemon
RCRSOURCEBB = ${RCRDAEMONCPP}
RCRSOURCECPP = blackboard.cpp wrmsr.cpp rdmsr.cpp RCRMSRGeneric.cpp
RCRSOURCEC =
RCRSOURCEH = blackboard.h blackboard.hpp
RCROBJS = ${RCRSOURCECPP:.cpp=.o} ${RCRSOURCEC:.c=.o} \
${RCRSOURCEBB:.cpp=.o}
RCROBJSCLEAN = ${RCRSOURCECPP:.cpp=.o} ${RCRSOURCEC:.c=.o}
# Input and output strings for EE_MPI library
EE_MPI_INPUT = ee_mpi
EE_MPI_OUTPUT = $(EE_MPI_INPUT)
# Source files for LIBEE_MPI_
EE_MPI_SOURCEINPUT = $(EE_MPI_INPUT).cpp
EE_MPI_SOURCECPP = basic_functions.cpp f2c_mpi.cpp measurement.cpp rdmsr.cpp\
RCR.bb.cpp RCRMSRGeneric.cpp wrmsr.cpp $(RCRDAEMON).cpp
EE_MPI_SOURCEC = downstream_api.c
EE_MPI_SOURCEF = $(EE_MPI_INPUT)_fort.f
OBJS = ${EE_MPI_SOURCECPP:.cpp=.o} ${EE_MPI_SOURCEC:.c=.o} \
${EE_MPI_SOURCEF:.f=.o} ${EE_MPI_SOURCEINPUT:.cpp=.o}
OBJSCLEAN = ${EE_MPI_SOURCECPP:.cpp=.o} ${EE_MPI_SOURCEC:.c=.o} \
${EE_MPI_SOURCEF:.f=.o} ${EE_MPI_SOURCEINPUT:.cpp=.o}
# Build RCRdaemon and Power Interface for target supplied in config file
#all : $(RCRDAEMON) RCRlogger ee_mpi-static ee_mpi-shared
all : $(RCRDAEMON) RCRlogger ee_mpi-shared object_clean
again : clean all
blackboard.cpp : blackboard.hpp
rcrSystemConfiguration.cpp : rcrSystemConfiguration.hpp
.c.o:
$(CC) $(OPT) -fPIC -c $<
.f.o:
$(FORT) $(OPT) -fPIC -I. -I$(INCLUDE) -c $<
.cpp.o:
$(CXX) $(OPT) -fPIC -I. -I$(INCLUDE) -c $<
# RCRDaemon for Haswell
$(RCRDAEMON): $(RCRDAEMON).o RCR.bb.o RCRMSRGeneric.o $(RCRSOURCEH) wrmsr.o
$(CXX) $(OPT) $(LIBS) -Bstatic -o $(RCRDAEMON) $(RCRDAEMON).o RCR.bb.o RCRMSRGeneric.o wrmsr.o
# Logger for Haswell
RCRlogger: RCRlogger.o RCR.bb.o RCRMSRGeneric.o $(RCRSOURCEH) wrmsr.o
$(CXX) $(OPT) $(LIBS) -O0 -Bstatic -o RCRlogger RCRlogger.o RCR.bb.o wrmsr.o -g
ee_mpi-static: $(OBJS)
$(AR) $(ARFLAGS) -o lib$(EE_MPI_OUTPUT).a $(OBJS)
ee_mpi-shared: $(OBJS)
$(CXX) $(SHARED_LIB_FLAGS) -o lib$(EE_MPI_OUTPUT).so $(NRM_LIBS) $(OBJS)
clean :
rm -f $(RCRDAEMON) RCRlogger lib*
# Clean only object files in current folder
object_clean :
rm *.o
This diff is collapsed.
#ifndef RENCI_RCRTOOL_BLACKBOARD_H
#define RENCI_RCRTOOL_BLACKBOARD_H
/* In Memory layout
blackboard struct
system meters
node 1
node 1 meters offset array
node 1 socket offset array
node 1 meters
node 1 socket 1
node 1 socket 1 meters offset array
node 1 socket 1 core offset array
node 1 socket 1 meters
node 1 socket 1 core 1
node 1 socket 1 core 1 meters offset array
node 1 socket 1 core 1 meters
node 1 socket 1 core 2
node 1 socket 1 core 2 meters offset array
node 1 socket 1 core 2 meters
node 1 socket 1 core 3
node 1 socket 1 core 3 meters offset array
node 1 socket 1 core 3 meters
node 1 socket 1 core 4
node 1 socket 1 core 4 meters offset array
node 1 socket 1 core 4 meters
node 1 socket 2
node 1 socket 2 meters offset array
node 1 socket 2 core offset array
node 1 socket 2 meters
node 1 socket 2 core 1
node 1 socket 2 core 1 meters offset array
node 1 socket 2 core 1 meters
node 1 socket 2 core 2
node 1 socket 2 core 2 meters offset array
node 1 socket 2 core 2 meters
node 1 socket 2 core 3
node 1 socket 2 core 3 meters offset array
node 1 socket 2 core 3 meters
node 1 socket 2 core 4
node 1 socket 2 core 4 meters offset array
node 1 socket 2 core 4 meters
node 2
node 2 meters offset array
node 2 socket offset array
node 2 meters
node 2 socket 1
node 2 socket 1 meters offset array
node 2 socket 1 core offset array
node 2 socket 1 meters
node 2 socket 1 core 1
node 2 socket 1 core 1 meters offset array
node 2 socket 1 core 1 meters
node 2 socket 1 core 2
node 2 socket 1 core 2 meters offset array
node 2 socket 1 core 2 meters
node 2 socket 1 core 3
node 2 socket 1 core 3 meters offset array
node 2 socket 1 core 3 meters
node 2 socket 1 core 4
node 2 socket 1 core 4 meters offset array
node 2 socket 1 core 4 meters
node 2 socket 2
node 2 socket 2 meters offset array
node 3 socket 2 core offset array
node 2 socket 2 meters
node 2 socket 2 core 1
node 2 socket 2 core 1 meters offset array
node 2 socket 2 core 1 meters
node 2 socket 2 core 2
node 2 socket 2 core 2 meters offset array
node 2 socket 2 core 2 meters
node 2 socket 2 core 3
node 2 socket 2 core 3 meters offset array
node 2 socket 2 core 3 meters
node 2 socket 2 core 4
node 2 socket 2 core 4 meters offset array
node 2 socket 2 core 4 meters
*/
#define RCRFILE_NAME "RCRFile"
const int RCRBlackBoardVersionNumber = 1; // incremented each time any fields are changed
const int MAX_RCRFILE_SIZE = 16384;
const int NODENAMEMAXLENGTH = 256;
struct _RCRBBMetaData {
int64_t bbVersionNumber;
int64_t bbTreeSize;
};
struct _RCRBlackboard {
struct _RCRBBMetaData md;
int64_t numBBMeters;
int64_t bbMeterOffset; // offset of array of meter offsets
int64_t numNodes;
int64_t nodeOffset; // offset of array of node offsets
};
struct _RCRNodeMetaData {
int64_t nodeType;
int64_t nodeNumber;
int64_t nodeTreeSize;
char * nodeName; //Node unique id. TODO: Change to double pointer.
};
struct _RCRNode {
struct _RCRNodeMetaData md;
int64_t numNodeMeters;
int64_t nodeMeterOffset; // offset of array of meter offsets
int64_t numSockets;
int64_t socketOffset; // offset of array of socket offsets
};
struct _RCRSocketMetaData {
int64_t socketType;
int64_t socketNumber;
int64_t socketTreeSize;
};
struct _RCRSocket {
struct _RCRSocketMetaData md;
int64_t numSocketMeters;
int64_t socketMeterOffset; // offset of array of meter offsets
int64_t numCores;
int64_t coreOffset; // offset of array of core offsets
};
struct _RCRCoreMetaData {
int64_t coreType;
int64_t coreNumber;
int64_t coreTreeSize;
};
struct _RCRCore // leaf hardware element -- only meters no children
{
struct _RCRCoreMetaData md;
int64_t numCoreMeters;
int64_t coreMeterOffset; // offset of array of meter offsets
int64_t numThreads;
int64_t threadOffset; // offset of array of core offsets
};
struct _RCRThreadMetaData {
int64_t threadType;
int64_t threadNumber;
int64_t threadTreeSize;
};
struct _RCRThread // leaf hardware element -- only meters no children
{
struct _RCRThreadMetaData md;
int64_t numThreadMeters;
int64_t threadMeterOffset; // offset of array of meter offsets
};
struct _RCROffset {
int64_t size;
int64_t offset[]; // id meter type
};
struct _RCRMeterValue {
int64_t meterID; // id meter type
// -- smaller field?? only if something else filling the 64 bits
union { // union for basically any 64-bit performance data to be saved
volatile int64_t iValue;
volatile uint64_t uValue;
volatile double fValue;
} data;
};
struct _RCRMeter {
int64_t size;
struct _RCRMeterValue value[]; // array of meters and their values
};
typedef struct _RCRMeter RCRMeter;
// defines for meter types
enum rcrMeterType {
DUMMY_SYSTEM,
DUMMY_NODE,
DUMMY_SOCKET,
DUMMY_CORE,
DUMMY_THREAD,
// RAPL counters
SOCKET_TEMP_STATUS,
SOCKET_ENERGY_STATUS,
NODE_RAPL_POWER_UNIT,
// Frequency counters
SOCKET_TSC,
SOCKET_MPERF,
SOCKET_APERF,
// Instruction counters
SOCKET_INSTRUCTIONS_RETIRED,
SOCKET_OS_INSTRUCTIONS_RETIRED,
// Memory counters
SOCKET_CONCURRENCY,
SOCKET_TOR_OCCUPANCY,
SOCKET_RR_OCCUPANCY,
SOCKET_LLC_VICTIMS,
CORE_TOR_OCCUPANCY,
CORE_RR_OCCUPANCY,
CORE_LLC_VICTIMS,
CORE_UNCORE_CLOCK_TICKS,
// Update counter
ENERGY_REFRESH_COUNTER,
MEMORY_REFRESH_COUNTER,
// Flags for MIC temperature.
DIE_TEMP,
BOARD_TEMP,
DEVICE_MEMORY_TEMP,
FAN_INTAKE_TEMP,
FAN_OUTLET_TEMP,
VCCP_TEMP,
VDDG_TEMP,
VDDQ_TEMP,
// Flags for MIC power.
INSTANTANEOUS_POWER,
PCIE_POWER,
TWO_THREE_CONNECTOR_POWER,
TWO_FOUR_CONNECTOR_POWER,
CORE_RAIL_POWER,
CORE_RAIL_CURRENT,
CORE_RAIL_VOLTAGE,
UNCORE_RAIL_POWER,
UNCORE_RAIL_CURRENT,
UNCORE_RAIL_VOLTAGE,
MEMORY_RAIL_POWER,
MEMORY_RAIL_CURRENT,
MEMORY_RAIL_VOLTAGE,
END
};
// string names for meter types -- index must match #define value above
/*
const char * typeString[] = { "memory concurrency", // 1
"end"}; // strings for printing
*/
// defines for Node Type
// defines for Socket Type
// defines for Core Type
// defines for Thread Type
#endif
#ifndef __QTHREAD_RCRTOOL_MSR_H__
#define __QTHREAD_RCRTOOL_MSR_H__
typedef enum _RCRCounter {
RCR_CNT_UNDEF = 0,
RCR_CNT_SOCKET,
RCR_CNT_CORE
} RCRCounter;
struct MSRCounter{
uint64_t name;
RCRCounter type;
uint32_t socket;
uint32_t core;
bool constant;
bool writable;
bool open;
int fd;
uint64_t value;
};
extern struct MSRCounter *msrArray;
uint64_t readMSRSocket(uint32_t socket, uint32_t name);
uint64_t readMSRCore(uint32_t core, off_t name);
struct MSRCounter* RCRSetup(uint32_t numSockets, uint32_t numCores, uint32_t maxCounters);
void writeMSRSocket(uint32_t socket, uint32_t name, uint64_t value);
void writeMSRCore(uint32_t core, uint32_t name, uint64_t value);
#endif
/**
* Intel SandyBridge specific RCR Daemon implementation
*
* By Allan Porterfield, David A. O'Brien and Min Yeol Lim
*
* Includes some code based on the UDP server example from the Linux Gazette
* (http://www.linuxgazette.com/node/8758)
* Uses some code based on the libpfm4 examples from Perfmon2
* (http://perfmon2.sourceforge.net/)
*/
#include <sys/types.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <time.h>
#include <err.h>
#include <fcntl.h>
#include <signal.h>
#include <iostream>
#include <errno.h>
#include <list>
#include "RCRMSR.h"
static uint32_t numberOfMSRS = 0;
static uint64_t read_msr(int fd, int which) {
uint64_t data;
if (pread(fd, &data, sizeof data, which) != sizeof data) {
perror("rdmsr:pread");
exit(127);
}
return data;
}
static int open_fd(struct MSRCounter *msr, bool write) {
int fd = -1;
char filename[BUFSIZ];
if (msr->open == false) {
sprintf(filename, "/dev/cpu/%d/msr_safe", msr->core);
if (!write) fd = open(filename, O_RDONLY);
else fd = open(filename, O_WRONLY);
if (fd >= 0) {
msr->fd = fd;
msr->open = true;
}
}
else fd = msr->fd;
// printf("\t\t\tfd %d open %d\n", msr->fd,msr->open);
return fd;
}
uint64_t readMSRSocket(uint32_t socket, uint32_t name) {
int i;
for (i = 0; i < numberOfMSRS; i++) {
// if (i <20) printf("entry %d name %d looking for %d\n", i,msrArray[i].name, name);
if (msrArray[i].name != name) continue; // not this counter
if (msrArray[i].type != RCR_CNT_SOCKET) continue; // not right type
if (msrArray[i].socket != socket) continue; // right name wrong socket
if (msrArray[i].open == false) {
msrArray[i].fd = open_fd(&msrArray[i], 0); // not open so open
if (msrArray[i].fd < 0) printf("MSR open failed %d %s\n", msrArray[i].fd, strerror(errno)); // open failed
}
if (msrArray[i].constant == false) { // counter constant -- value already valid
msrArray[i].value = read_msr(msrArray[i].fd, msrArray[i].name);
}
return msrArray[i].value;
}
return 0;
}
uint64_t readMSRCore(uint32_t core, off_t name) {
char msr_file_name[64];
ssize_t retval;
uint64_t data;
int fd_msr;
sprintf(msr_file_name, "/dev/cpu/%d/msr_safe", core);
fd_msr = open(msr_file_name, O_RDONLY);
if (fd_msr < 0) {
if (errno == ENXIO) {
fprintf(stderr, "RCRTool: No CPU %d\n", core);
return -1;
} else if (errno == EIO) {
fprintf(stderr, "RCRTool: CPU %d doesn't support MSRs\n",
core);
return -1;
} else {
perror("RCRTool: ");
return -1;
}
}
retval = pread(fd_msr, &data, sizeof data, name);
if (retval != sizeof data) {
fprintf(stderr, "pread cpu%d 0x%x = %d\n", core, name, retval);
exit (-2);
}
close(fd_msr);
return data;
}
/*uint64_t readMSRCore(uint32_t core, uint32_t name) {
int i;
for (i = 0; i < numberOfMSRS; i++) {
// if (i <20) printf("entry %d name %d looking for %d\n", i,msrArray[i].name, name);
if (msrArray[i].name != name) continue; // not this counter
if (msrArray[i].type != RCR_CNT_CORE) continue; // not right type
if (msrArray[i].core != core) continue; // right name wrong socket
if (msrArray[i].open == false) {
msrArray[i].fd = open_fd(&msrArray[i], 0); // not open so open
if (msrArray[i].fd < 0) printf("MSR open failed \n"); // open failed
}
if (msrArray[i].constant == false) { // counter constant -- value already valid
msrArray[i].value = read_msr(msrArray[i].fd, msrArray[i].name);
}
return msrArray[i].value;
}
}*/
static bool RCRSetupCalled = false;
struct MSRCounter* RCRSetup(uint32_t numSockets, uint32_t numCores, uint32_t maxCounters) {
int i;
if (RCRSetupCalled) return msrArray; // already setup
if (maxCounters == 0) maxCounters = 8;
RCRSetupCalled = true;
numberOfMSRS = numSockets * numCores * maxCounters; // for starts allow 8 per core -- AKP Need to compute correct amount
msrArray = (struct MSRCounter*) malloc(numberOfMSRS * sizeof(struct MSRCounter));
for (i = 0; i < numberOfMSRS; i++) {
msrArray[i].name = -1;
msrArray[i].type = RCR_CNT_UNDEF;
msrArray[i].socket = -1;
msrArray[i].core = -1;
msrArray[i].constant = false;
msrArray[i].writable = false;
msrArray[i].open = false;
msrArray[i].fd = -1;
msrArray[i].value = 0;
}
return msrArray;
}
static void write_msr(int fd, int which, uint64_t value) {
if (pwrite(fd, &value, sizeof(uint64_t), which) != sizeof(uint64_t)) {
perror("wrmsr:pwrite");
exit(127);
}
return;
}
void writeMSRSocket(uint32_t socket, uint32_t name, uint64_t value) {
int i;
for (i = 0; i < numberOfMSRS; i++) {
// if (i <20) printf("entry %d name %d looking for %d\n", i,msrArray[i].name, name);
if (msrArray[i].name != name) continue; // not this counter
if (msrArray[i].type != RCR_CNT_SOCKET) continue; // not right type
if (msrArray[i].socket != socket) continue; // right name wrong socket
if (msrArray[i].open == false) {
msrArray[i].fd = open_fd(&msrArray[i], 1); // not open so open
if (msrArray[i].fd < 0) printf("MSR open failed \n"); // open failed
}
if (msrArray[i].constant == false) { // counter constant -- value already valid
write_msr(msrArray[i].fd, msrArray[i].name, value);
}
return;
}
}
void writeMSRCore(uint32_t core, uint32_t name, uint64_t value) {
int i;
for (i = 0; i < numberOfMSRS; i++) {
// if (i <20) printf("entry %d name %d looking for %d\n", i,msrArray[i].name, name);
if (msrArray[i].name != name) continue; // not this counter
if (msrArray[i].type != RCR_CNT_SOCKET) continue; // not right type
if (msrArray[i].core != core) continue; // right name wrong socket
if (msrArray[i].open == false) {
msrArray[i].fd = open_fd(&msrArray[i], 1); // not open so open
if (msrArray[i].fd < 0) printf("MSR open failed \n"); // open failed
}
if (msrArray[i].constant == false) { // counter constant -- value already valid
write_msr(msrArray[i].fd, msrArray[i].name, value);
}
return;
}
}
#ifndef RCRBLACKBOARD_H
#define RCRBLACKBOARD_H
#include <stdint.h> // for int64_t
#include <stdio.h> // for printf
#include <stdlib.h> // for malloc - temp until share memory region allocated
#include <sys/mman.h> // for mmap
#include <fcntl.h> // for mmap #defs
#include <unistd.h> // for ftruncate
#include "RCR.bb.h"
using namespace std;
class RCRblackboard {
private:
struct _RCRBlackboard *bb; // pointer to _RCRBlackboard in shared memory
int64_t allocationHighWater;
void * bbMem;
public:
RCRblackboard(); // allocates shared memory and creates blackboard
RCRblackboard(const RCRblackboard &bb); // allocates shared memory and creates blackboard
~RCRblackboard();
// Punting on protection -- trusing readers to only read -- eventually use the OS
// page protection to prevent writing by readers
// return address of RCRmeter for later direct use
volatile int64_t *getThreadMeter(enum rcrMeterType id, int64_t node, int64_t socket,
int64_t core, int64_t thread); // get a specific thread meter
volatile int64_t *getCoreMeter(enum rcrMeterType id, int64_t node, int64_t socket,
int64_t core); // tet a specific core meter
volatile int64_t *getSocketMeter(enum rcrMeterType id, int64_t node, int64_t socket
); // get a specific socket meter
volatile int64_t *getNodeMeter(enum rcrMeterType id, int64_t node); // get a specific node meter
volatile int64_t *getSystemMeter(enum rcrMeterType id); // get a specific system meter
int64_t getNumOfNodes();
int64_t getNumOfSockets();
int64_t getNumOfCores();
int64_t getNumOfThreads();
char* getNodeName();
int64_t setSystemSize(int64_t initOffset);
int64_t setNodeSize(int64_t initOffset);
int64_t setSocketSize(int64_t initOffset);
int64_t setCoreSize(int64_t initOffset);
int64_t setThreadSize(int64_t initOffset);
int64_t setNodeOffset(int64_t nodeID);
int64_t setSocketOffset(int64_t nodeID, int64_t socketID);
int64_t setCoreOffset(int64_t nodeID, int64_t socketID, int64_t coreID);
int64_t setThreadOffset(int64_t nodeID, int64_t socketID, int64_t coreID, int64_t threadID);
// assign a meter for each thread -- returns true if all assigned
bool assignSystemMeter(enum rcrMeterType id);
bool assignNodeMeter(int64_t nodeId, enum rcrMeterType id);
bool assignSocketMeter(int64_t nodeId, int64_t socketId, enum rcrMeterType id);
bool assignCoreMeter(int64_t nodeId, int64_t socketId,
int64_t coreId, enum rcrMeterType id);
bool assignThreadMeter(int64_t nodeId, int64_t socketId,
int64_t coreId, int64_t threadId, enum rcrMeterType id);
// Initial implementation builds a homogeneous system -- need a heterogeneous build
// sometime in the future (probably a bottom-up build rather than the current
// top-down version
int64_t buildThread(int64_t threadId, int64_t numThreadMeters);
int64_t buildCore(int64_t c, int64_t coreMeters, int64_t numThread);
int64_t buildSocket(int64_t s, int64_t socketMeters, int64_t numCore);
int64_t buildNode(int64_t n, int64_t nodeMeters, int64_t numSocket);
int64_t buildSystem(int64_t numBBMeters, int64_t numNodes);
// build a homogeneous blackboard structure in shared memory
bool buildSharedMemoryBlackBoard(int64_t systemMeters,
int64_t numNode, int64_t nodeMeters,
int64_t numSocket, int64_t socketMeters,
int64_t numCore, int64_t coreMeters,
int64_t numThread, int64_t threadMeters
);
// read existing blackboard
bool initBlackBoard();
private:
char * getMacAddress();
char * base();
int64_t allocateSharedMemory(int64_t size);
int64_t getCurOffset();
struct _RCRNode * getNode(int nodeId);
struct _RCRSocket * getSocket(int nodeId, int socketId);
struct _RCRCore * getCore(int nodeId, int socketId, int coreId);
struct _RCRThread * getThread(int nodeId, int socketId, int coreId, int threadId);
};
#endif
This diff is collapsed.
This diff is collapsed.
# MPI library to use ARGO Node Resource Manager (MPI-NRM) downstream API
This Message Passing Interface (MPI) library allows application of runtime
policies for energy efficiency through the MPI standard profiling interface
(PMPI).
The current implementation passes phase contextual information (compute and
barrier times) to the Argo Node Resource Manager (NRM). The NRM using this
contextual information invokes power policies to improve energy efficiency
of the node.
The power policies in NRM need contextual information from the application
(e.g. time spent doing computation and in the barrier during a phase) for
decision. This information from the application can be provided to NRM using
a C downstream API.
This MPI library written in C/C++ uses the MPI Profiling
Interface (PMPI) to intercept MPI calls and sends the application context
information to NRM using the C downstream API.
## Requirements
The C downstream API uses ZeroMQ (http://www.zeromq.org) to transmit messages
to NRM. So it needs to be installed in addition to any stable MPI version
(however, it has been only tested with MPICH 3.2.1).
Update the following paths in Makefile to point to your ZeroMQ and MPI
installations respectively.
NRM_LIBS =
&
INCLUDE =
Calling a `make` then should create a `libmpi_nrm.so` shared library file.
## Basic Usage
This shared library file can then be used with the the NRM
(https://xgitlab.cels.anl.gov/argo/nrm/tree/master) by manually updating the
applicationpreloadlibrary = ''
in `containers.py` at the moment.
The need to manually update will be fixed in the future.
`export OperationMode=0`
to not transmit application context information.
or
`export OperationMode=1`
to transmit application context information.
## Additional Info
Report bugs to Sridutt Bhalachandra (<sriduttb@anl.gov>)
/* Filename: basic_functions.ccpp
*
* Basic Description: Contains functions common to all policies
*
*/