├── bash.sh ├── versions.sh ├── parse.py ├── test.sh ├── test.docker ├── push.sh ├── phylanx.devenv ├── auto.sh ├── apex.devenv └── activeharmony-4.6.0 └── code-server └── code_generator.cxx /bash.sh: -------------------------------------------------------------------------------- 1 | if [ "$USER" = "" ] 2 | then 3 | export USER=jovyan 4 | fi 5 | if [ "$HOME" = "" ] 6 | then 7 | export HOME=/home/jovyan 8 | fi 9 | -------------------------------------------------------------------------------- /versions.sh: -------------------------------------------------------------------------------- 1 | for v in /hpx /blaze /blaze_tensor /pybind11 ~/phylanx 2 | do 3 | echo -n "VERSION: $v " 4 | cd $v 5 | git log -1 --format=%cd 6 | done 7 | -------------------------------------------------------------------------------- /parse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import re 3 | import sys 4 | fails = 100 5 | success = False 6 | with open(sys.argv[1],"r") as fd: 7 | for line in fd.readlines(): 8 | g = re.search(r'tests passed, (\d+) tests failed out of',line) 9 | if g: 10 | fails = int(g.group(1)) 11 | success = True 12 | print("Failed:",fails) 13 | 14 | if success and fails < 5: 15 | exit(0) 16 | else: 17 | print("No tests passed") 18 | exit(1) 19 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd 3 | set -x 4 | 5 | export BUILD_DIR=/usr/local/build 6 | export INSTALL_DIR=/usr/local/phylanx 7 | export PYTHONUSERBASE=/usr/local/userbase 8 | #export BRANCH=distributed_performance 9 | 10 | # I don't understand why this is necessary 11 | export PYTHON_DIR=$(python3 -c 'import sys; print("/usr/local/userbase/lib/python%d.%d/site-packages" % (sys.version_info.major, sys.version_info.minor))') 12 | 13 | mkdir -p $PYTHON_DIR 14 | build.sh config install 15 | build.sh tests > test-out.txt 2>&1 16 | bash versions.sh >> test-out.txt 17 | cd ~/phylanx 18 | 19 | # Clean up build 20 | cd $BUILD_DIR 21 | rm -f $(find . -type f -a ! \( -name \*.so\* -o -name physl \) ) 22 | -------------------------------------------------------------------------------- /test.docker: -------------------------------------------------------------------------------- 1 | ARG IMAGE 2 | FROM $IMAGE 3 | USER root 4 | COPY versions.sh /usr/local/bin/ 5 | RUN chmod 755 /usr/local/bin/versions.sh 6 | RUN chmod 755 /home/jovyan 7 | RUN mkdir /usr/local/phylanx 8 | RUN mkdir /usr/local/userbase 9 | RUN mkdir /usr/local/build 10 | RUN chown jovyan /usr/local/phylanx 11 | RUN chown jovyan /usr/local/userbase 12 | RUN chown jovyan /usr/local/build 13 | USER jovyan 14 | #RUN bash versions.sh >> test-out.txt 15 | ARG BUILD_TYPE 16 | ENV BUILD_TYPE=$BUILD_TYPE 17 | ARG CPUS 18 | ENV CPUS=$CPUS 19 | COPY test.sh . 20 | RUN bash ./test.sh 21 | USER root 22 | RUN find /home/jovyan -type d| xargs chmod og+rx 23 | RUN find /home/jovyan -type f| xargs chmod og+r 24 | USER jovyan 25 | #RUN cp /home/jovyan/install/phylanx/bin/physl /home/jovyan/phylanx/build/bin/physl 26 | #RUN chmod 755 /home/jovyan/phylanx/build/bin/physl 27 | -------------------------------------------------------------------------------- /push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$DOCKER_HUB_ACCT" != "" ] 4 | then 5 | docker push ${DOCKER_HUB_ACCT}phylanx${TAG}.devenv:working 6 | docker tag phylanx-test${TAG} ${DOCKER_HUB_ACCT}phylanx${TAG}.test:working 7 | docker push ${DOCKER_HUB_ACCT}phylanx${TAG}.test:working 8 | fi 9 | 10 | # Push to remote machine if there is one. 11 | if [ "${REMOTE}" != "" ] 12 | then 13 | ssh ${REMOTE} singularity build -F ~/images/phylanx-devenv${TAG}.simg docker://${DOCKER_HUB_ACCT}phylanx${TAG}.devenv:working 14 | ssh ${REMOTE} chmod 755 ~/images/phylanx-devenv${TAG}.simg 15 | 16 | ssh ${REMOTE} singularity build -F ~/images/phylanx-test${TAG}.simg docker://${DOCKER_HUB_ACCT}phylanx${TAG}.test:working 17 | ssh ${REMOTE} chmod 755 ~/images/phylanx-test${TAG}.simg 18 | #ssh ${REMOTE} bin/build-image.sh ~/images/phylanx-devenv.simg docker://${DOCKER_HUB_ACCT}phylanx${TAG}.devenv:working 19 | fi 20 | -------------------------------------------------------------------------------- /phylanx.devenv: -------------------------------------------------------------------------------- 1 | FROM fedora 2 | RUN dnf install -y gcc-c++ gcc make cmake git \ 3 | bzip2 hwloc-devel blas blas-devel lapack lapack-devel boost-devel \ 4 | libatomic which compat-openssl10 vim-enhanced wget zlib-devel \ 5 | python3-flake8 gdb sudo python36 openmpi-devel sqlite-devel sqlite \ 6 | findutils openssl-devel lm_sensors-devel 7 | 8 | ARG CPUS 9 | ARG BUILD_TYPE 10 | 11 | RUN ln -s /usr/lib64/openmpi/lib/libmpi_cxx.so /usr/lib64/openmpi/lib/libmpi_cxx.so.1 12 | RUN ln -s /usr/lib64/openmpi/lib/libmpi.so /usr/lib64/openmpi/lib/libmpi.so.12 13 | ENV PYVER 3.6.8 14 | RUN wget https://www.python.org/ftp/python/${PYVER}/Python-${PYVER}.tgz 15 | RUN tar xf Python-${PYVER}.tgz 16 | WORKDIR /Python-${PYVER} 17 | RUN ./configure 18 | RUN make -j ${CPUS} install 19 | 20 | # Make headers available 21 | RUN cp /Python-${PYVER}/pyconfig.h /Python-${PYVER}/Include 22 | RUN ln -s /Python-${PYVER}/Include /usr/include/python${PYVER} 23 | 24 | RUN pip3 install --trusted-host pypi.org --trusted-host files.pythonhosted.org numpy tensorflow keras CNTK pytest 25 | RUN pip3 install numpy tensorflow keras CNTK pytest 26 | WORKDIR / 27 | 28 | RUN git clone --depth 1 https://github.com/STEllAR-GROUP/hpx.git && \ 29 | mkdir -p /hpx/build && \ 30 | cd /hpx/build && \ 31 | cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ 32 | -DHPX_WITH_MALLOC=system \ 33 | -DHPX_WITH_MORE_THAN_64_THREADS=ON \ 34 | -DHPX_WITH_MAX_CPU_COUNT=80 \ 35 | -DHPX_WITH_EXAMPLES=Off \ 36 | .. && \ 37 | make -j ${CPUS} install && \ 38 | rm -f $(find . -name \*.o) 39 | 40 | RUN git clone --depth 1 https://github.com/pybind/pybind11.git && \ 41 | mkdir -p /pybind11/build && \ 42 | cd /pybind11/build && \ 43 | cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DPYBIND11_PYTHON_VERSION=${PYVER} .. && \ 44 | make -j ${CPUS} install && \ 45 | rm -f $(find . -name \*.o) 46 | 47 | RUN git clone --depth 1 https://bitbucket.org/blaze-lib/blaze.git && \ 48 | cd /blaze && \ 49 | mkdir -p /blaze/build && \ 50 | cd /blaze/build && \ 51 | cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .. && \ 52 | make -j ${CPUS} install && \ 53 | rm -f $(find . -name \*.o) 54 | 55 | RUN git clone --depth 1 https://github.com/STEllAR-GROUP/blaze_tensor.git && \ 56 | mkdir -p /blaze_tensor/build && \ 57 | cd /blaze_tensor/build && \ 58 | cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .. && \ 59 | make -j ${CPUS} install && \ 60 | rm -f $(find . -name \*.o) 61 | 62 | COPY build.sh /usr/local/bin/build.sh 63 | RUN chmod +x /usr/local/bin/build.sh 64 | 65 | RUN echo "ALL ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers 66 | COPY phylanx.devenv /Dockerfile 67 | RUN useradd -m jovyan 68 | USER jovyan 69 | WORKDIR /home/jovyan 70 | ENV LD_LIBRARY_PATH /home/jovyan/install/phylanx/lib64:/usr/local/lib64:/home/jovyan/install/phylanx/lib/phylanx:/usr/lib64/openmpi/lib 71 | COPY bash.sh /home/jovyan/.bashrc 72 | CMD ["sleep","infinity"] 73 | -------------------------------------------------------------------------------- /auto.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Configuration 4 | # Find execution dir 5 | export INSTALL_DIR=$(dirname $(realpath $0)) 6 | if [ "$EMAIL" = "" ] 7 | then 8 | export EMAIL=$(grep 'email.*=' ~/.gitconfig|cut -d= -f2|sed 's/\s//g') 9 | else 10 | echo "PLEASE SET UP YOUR GIT EMAIL" 11 | echo "git config --global user.email johndoe@example.com" 12 | exit 2 13 | fi 14 | for bt in Release Debug 15 | do 16 | export BUILD_TYPE=$bt 17 | for img in apex # apex noapex f30 18 | do 19 | 20 | echo "=====[BEGIN $bt $img]=====" 21 | # Here we want the dockerhub account with a / or an empty string. 22 | if [ "$DOCKER_HUB_ACCT" = "" ] 23 | then 24 | export DOCKER_HUB_ACCT=$(docker info|grep Username:|cut -d: -f2|sed 's/\s//g'|sed 's|/*$|/|') 25 | fi 26 | 27 | export CPUS=4 #$(($(lscpu|grep '^CPU(s):'|cut -d: -f2|sed 's/\s//g')/2)) 28 | 29 | echo "CONFIGURATION INFO:" 30 | echo "EMAIL=($EMAIL)" 31 | echo "INSTALL_DIR=($INSTALL_DIR)" 32 | echo "DOCKER_HUB_ACCT=($DOCKER_HUB_ACCT)" 33 | echo "CPUS=${CPUS}" 34 | 35 | source ~/.bashrc 36 | set -e 37 | set -x 38 | cd $INSTALL_DIR 39 | if [ "$BUILD_TYPE" = "Release" ] 40 | then 41 | export TAG=.rel 42 | else 43 | export TAG= 44 | fi 45 | if [ "$img" = "noapex" ] 46 | then 47 | export TAG="$TAG.noapex" 48 | fi 49 | if [ "$img" = "f30" ] 50 | then 51 | export TAG="$TAG.f30" 52 | fi 53 | echo "BUILD_TYPE=$BUILD_TYPE, TAG=$TAG" 54 | if [ "$img" = "f30" ] 55 | then 56 | docker pull fedora:30 57 | else 58 | docker pull fedora:26 59 | fi 60 | docker build --no-cache --build-arg CPUS=$CPUS --build-arg BUILD_TYPE=$BUILD_TYPE -f ${img}.devenv -t ${DOCKER_HUB_ACCT}phylanx${TAG}.devenv . 61 | 62 | docker build --build-arg CPUS=$CPUS --build-arg BUILD_TYPE=$BUILD_TYPE --build-arg IMAGE=${DOCKER_HUB_ACCT}phylanx${TAG}.devenv -f test.docker -t phylanx-test${TAG} . 63 | docker run --rm phylanx-test${TAG} cat test-out.txt > test-out-$bt-$img.txt 64 | cp test-out-$bt-$img.txt test-out.txt 65 | echo $EMAIL > email-body-$bt-$img.html 66 | echo 'Phylanx Build Status' >> email-body-$bt-$img.html 67 | rm -f email-body-$bt-$img.txt 68 | touch email-body-$bt-$img.txt 69 | set +e 70 | python3 parse.py test-out-$bt-$img.txt 71 | if [ $? = 0 ] 72 | then 73 | docker tag ${DOCKER_HUB_ACCT}phylanx${TAG}.devenv:latest ${DOCKER_HUB_ACCT}phylanx${TAG}.devenv:working 74 | bash push.sh 75 | set +e 76 | docker stop devenv 77 | sleep 5 78 | docker run --name devenv --privileged -v devenv_homefs-phylanx:/home/jovyan -d --rm ${DOCKER_HUB_ACCT}phylanx${TAG}.devenv:working 79 | set -e 80 | echo "Succeeded in building phylanx $bt $img." >> email-body-$bt-$img.txt 81 | else 82 | echo "Failed to build phylanx $bt $img." >> email-body-$bt-$img.txt 83 | fi 84 | echo '' >> email-body-$bt-$img.txt 85 | touch test-out-$bt-$img.txt 86 | tail -20 test-out-$bt-$img.txt >> email-body-$bt-$img.txt 87 | mail -s "Auto Update Phylanx for $bt $img" ${EMAIL} < email-body-$bt-$img.txt 88 | echo "=====[END $bt $img]=====" 89 | done 90 | done 91 | -------------------------------------------------------------------------------- /apex.devenv: -------------------------------------------------------------------------------- 1 | FROM fedora:26 2 | RUN dnf install -y gcc-c++ gcc make git \ 3 | bzip2 hwloc-devel blas blas-devel lapack lapack-devel \ 4 | libatomic which compat-openssl10 vim-enhanced wget zlib-devel \ 5 | python3-flake8 gdb sudo python36 mpich-devel sqlite-devel sqlite \ 6 | findutils openssl-devel papi papi-devel lm_sensors-devel hdf5-devel \ 7 | libffi-devel file hostname 8 | 9 | ARG CPUS 10 | ARG BUILD_TYPE 11 | WORKDIR / 12 | 13 | ENV CMAKE_VER 3.17.0 14 | RUN curl -LO http://www.cmake.org/files/v$(echo $CMAKE_VER|cut -d. -f1,2)/cmake-${CMAKE_VER}.tar.gz 15 | RUN tar xzf cmake-${CMAKE_VER}.tar.gz 16 | WORKDIR /cmake-${CMAKE_VER} 17 | RUN ./configure && make -j ${CPUS} && make install 18 | WORKDIR / 19 | 20 | ENV BOOST_VER 1_74_0 21 | WORKDIR /usr/install 22 | COPY boost_${BOOST_VER}.tar.bz2 . 23 | RUN tar xjf boost_${BOOST_VER}.tar.bz2 24 | WORKDIR /usr/install/boost_${BOOST_VER} 25 | ENV CPLUS_INCLUDE_PATH /usr/include/python3.5m 26 | RUN bash ./bootstrap.sh 27 | RUN ./b2 -j 5 install 28 | WORKDIR / 29 | 30 | RUN git clone https://github.com/BlueBrain/HighFive.git 31 | WORKDIR /HighFive/build 32 | RUN cmake .. 33 | RUN make install 34 | WORKDIR / 35 | 36 | ENV AH_VER 4.6.0 37 | RUN curl -kLO https://www.dyninst.org/sites/default/files/downloads/harmony/ah-${AH_VER}.tar.gz 38 | RUN tar -xzf ah-${AH_VER}.tar.gz 39 | WORKDIR /activeharmony-${AH_VER} 40 | COPY activeharmony-4.6.0/code-server/code_generator.cxx code-server/code_generator.cxx 41 | RUN make CFLAGS=-fPIC LDFLAGS=-fPIC && make install prefix=/usr/local/activeharmony 42 | 43 | ENV OTF2_VER 2.1.1 44 | WORKDIR / 45 | RUN curl -kLO https://www.vi-hps.org/cms/upload/packages/otf2/otf2-${OTF2_VER}.tar.gz 46 | RUN tar -xzf otf2-${OTF2_VER}.tar.gz 47 | WORKDIR otf2-${OTF2_VER} 48 | RUN ./configure --prefix=/usr/local/otf2 --enable-shared && make && make install 49 | 50 | WORKDIR / 51 | 52 | #RUN ln -s /usr/lib64/openmpi/lib/libmpi_cxx.so /usr/lib64/openmpi/lib/libmpi_cxx.so.1 53 | #RUN ln -s /usr/lib64/openmpi/lib/libmpi.so /usr/lib64/openmpi/lib/libmpi.so.12 54 | ENV PYVER 3.8.6 55 | RUN wget https://www.python.org/ftp/python/${PYVER}/Python-${PYVER}.tgz 56 | RUN tar xf Python-${PYVER}.tgz 57 | WORKDIR /Python-${PYVER} 58 | RUN ./configure 59 | RUN make -j ${CPUS} install 60 | 61 | # Make headers available 62 | RUN cp /Python-${PYVER}/pyconfig.h /Python-${PYVER}/Include 63 | RUN ln -s /Python-${PYVER}/Include /usr/include/python${PYVER} 64 | 65 | #RUN pip3 install --trusted-host pypi.org --trusted-host files.pythonhosted.org numpy tensorflow keras pytest 66 | RUN pip3 install numpy tensorflow keras pytest 67 | WORKDIR / 68 | 69 | ENV PATH /usr/lib64/mpich/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:. 70 | RUN git clone --depth 1 https://github.com/STEllAR-GROUP/hpx.git && \ 71 | mkdir -p /hpx/build && \ 72 | cd /hpx/build && \ 73 | cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ 74 | -DHPX_WITH_PARCELPORT_MPI=ON \ 75 | -DHPX_WITH_MALLOC=system \ 76 | -DHPX_WITH_MORE_THAN_64_THREADS=ON \ 77 | -DHPX_WITH_MAX_CPU_COUNT=80 \ 78 | -DHPX_WITH_EXAMPLES=Off \ 79 | -DHPX_WITH_APEX=TRUE \ 80 | -DAPEX_WITH_ACTIVEHARMONY=TRUE \ 81 | -DACTIVEHARMONY_ROOT=/usr/local/activeharmony \ 82 | -DAPEX_WITH_OTF2=TRUE \ 83 | -DOTF2_ROOT=/usr/local/otf2 \ 84 | -DAPEX_USE_CLOCK_TIMESTAMP=TRUE \ 85 | -DAPEX_WITH_PAPI=TRUE \ 86 | -DHPX_WITH_APEX_NO_UPDATE=TRUE \ 87 | -DHPX_WITH_APEX_TAG=develop \ 88 | -DAPEX_WITH_BFD=FALSE \ 89 | .. && \ 90 | make -j ${CPUS} install && \ 91 | rm -f $(find . -name \*.o) 92 | 93 | RUN git clone --depth 1 https://github.com/pybind/pybind11.git && \ 94 | mkdir -p /pybind11/build && \ 95 | cd /pybind11/build && \ 96 | cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DPYBIND11_PYTHON_VERSION=${PYVER} .. && \ 97 | make -j ${CPUS} install && \ 98 | rm -f $(find . -name \*.o) 99 | 100 | RUN git clone --depth 1 https://bitbucket.org/blaze-lib/blaze.git && \ 101 | cd /blaze && \ 102 | mkdir -p /blaze/build && \ 103 | cd /blaze/build && \ 104 | cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .. && \ 105 | make -j ${CPUS} install && \ 106 | rm -f $(find . -name \*.o) 107 | 108 | RUN git clone --depth 1 https://github.com/STEllAR-GROUP/blaze_tensor.git && \ 109 | mkdir -p /blaze_tensor/build && \ 110 | cd /blaze_tensor/build && \ 111 | cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .. && \ 112 | make -j ${CPUS} install && \ 113 | rm -f $(find . -name \*.o) 114 | 115 | COPY build-${BUILD_TYPE}.sh /usr/local/bin/build.sh 116 | RUN chmod +x /usr/local/bin/build.sh 117 | 118 | RUN echo "ALL ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers 119 | COPY phylanx.devenv /Dockerfile 120 | RUN useradd -m jovyan -d /home/jovyan 121 | #RUN ln /usr/lib64/libhwloc.so /usr/lib64/libhwloc.so.5 122 | USER jovyan 123 | WORKDIR /home/jovyan 124 | ENV LD_LIBRARY_PATH /home/jovyan/install/phylanx/lib64:/usr/local/lib64:/home/jovyan/install/phylanx/lib/phylanx:/usr/lib64/mpich/lib 125 | COPY bash.sh /home/jovyan/.bashrc 126 | CMD ["sleep","infinity"] 127 | -------------------------------------------------------------------------------- /activeharmony-4.6.0/code-server/code_generator.cxx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2003-2016 Jeffrey K. Hollingsworth 3 | * 4 | * This file is part of Active Harmony. 5 | * 6 | * Active Harmony is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published 8 | * by the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * Active Harmony is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with Active Harmony. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include "hspace.h" 46 | #include "hcfg.h" 47 | #include "hmesg.h" 48 | #include "hpoint.h" 49 | #include "hsockutil.h" 50 | #include "hutil.h" 51 | 52 | /* 53 | * Session and configuration information from the Harmony Server. 54 | */ 55 | using namespace std; 56 | 57 | typedef struct { 58 | int pid; 59 | int step; 60 | hmesg_t mesg; 61 | string hostname; 62 | } generator_t; 63 | 64 | typedef struct { 65 | string path; 66 | string host; 67 | string user; 68 | string port; 69 | } url_t; 70 | 71 | /* 72 | * Internal helper function prototypes. 73 | */ 74 | void generator_main(generator_t& gen); 75 | int codeserver_init(string& filename); 76 | int dir_erase(string& dirname); 77 | int parse_slave_list(const char* hostlist); 78 | int slave_complete(pid_t pid); 79 | vector values_of(const hpoint_t* pt); 80 | string vector_to_string(vector& v); 81 | string vector_to_bash_array_local(vector& v); 82 | string vector_to_bash_array_remote(vector& v); 83 | int file_type(const char* fileName); 84 | void logger(const string& message); 85 | double time_stamp(void); 86 | int url_parse(const char* buf, url_t& url); 87 | int mesg_write(hmesg_t& mesg, int step); 88 | int mesg_read(const char* filename, hmesg_t* msg); 89 | int read_loop(int fd, char* buf, int len); 90 | int write_loop(int fd, char* buf, int len); 91 | 92 | /* 93 | * Global Variable Declaration 94 | */ 95 | const char infile_name[] = "candidate"; 96 | const char outfile_name[] = "code_complete"; 97 | unsigned nchildren = 1; 98 | int timestep = 0; 99 | vector gen_list; 100 | 101 | string log_file; 102 | stringstream log_message; 103 | 104 | /* 105 | * Configuration values passed in from the Harmony server. 106 | */ 107 | string appname, slave_path; 108 | url_t local_url, reply_url, target_url; 109 | 110 | /* 111 | * generators: These are the real work-horses. For each new configuration, we 112 | * fork a new process to generate code. These processes die after the code 113 | * generation is complete. We block all the signals because we do not want to 114 | * interrupt any code generation activity. 115 | */ 116 | pid_t generator_make(generator_t& gen) 117 | { 118 | pid_t pid; 119 | 120 | pid = fork(); 121 | if (pid > 0) { 122 | // Parent case. 123 | gen.pid = pid; 124 | return pid; 125 | } 126 | else if (pid < 0) { 127 | // Error case. 128 | cerr << "Error on fork(); " << strerror(errno) << "\n"; 129 | return 0; 130 | } 131 | 132 | generator_main(gen); // Child continues. 133 | return 0; 134 | } 135 | 136 | /* 137 | * This gets the code generation parameters from the code manager and 138 | * fires scripts to use the underlying code generation tool to 139 | * generate the code. Scripts for different code and different 140 | * code-generation utility need to be provided by the user. 141 | * 142 | * This is where the code generation happens. Note that appname has 143 | * to match the name given to this session. 144 | */ 145 | void generator_main(generator_t& gen) 146 | { 147 | // Make a call to chill_script.appname.sh 148 | vector values = values_of(gen.mesg.data.point); 149 | 150 | // Print a message to the logger. 151 | log_message.str(""); 152 | log_message << gen.hostname << ": " << vector_to_string(values) << "\n"; 153 | logger(log_message.str()); 154 | 155 | // Set which machine to use. 156 | // First check to see if there is an underscore in the machine name. 157 | string generator_name; 158 | generator_name = gen.hostname.substr(0, gen.hostname.find("_")); 159 | 160 | // Different machines might be configured differently. So a check 161 | // here should be made to make sure that the hostname matches 162 | // uniformly across generator_hosts file and hostname gathered 163 | // here. 164 | 165 | // Determine if slave is on a remote host. 166 | bool flag = (generator_name == local_url.host); 167 | 168 | stringstream ss; 169 | ss.str(""); 170 | 171 | if (!flag) { 172 | // Remote. 173 | ss << "ssh " << generator_name << " "; 174 | } 175 | ss << "exec " << slave_path << "/" << gen.hostname 176 | << "_" << appname << "/chill_script." << appname << ".sh "; 177 | 178 | if (flag) { 179 | ss << vector_to_bash_array_local(values); 180 | } else { 181 | ss << vector_to_bash_array_remote(values); 182 | } 183 | 184 | ss << generator_name << " " 185 | << slave_path << "/" << gen.hostname << "_" << appname << " " 186 | << target_url.host << " " 187 | << target_url.path; 188 | 189 | cout << "Executing: " << ss.str() << endl; 190 | int sys_return = system(ss.str().c_str()); 191 | cout << "Returned: " << sys_return << endl; 192 | 193 | while (gen_list.size()) { 194 | hmesg_fini(&gen_list.back().mesg); 195 | gen_list.pop_back(); 196 | } 197 | 198 | // Error check not done yet. 199 | exit(0); 200 | } 201 | 202 | int main(int argc, char* argv[]) 203 | { 204 | stringstream ss; 205 | int status, num_ready = 0; 206 | unsigned i; 207 | pid_t pid; 208 | 209 | if (argc != 2) { 210 | cerr << "Usage: ./code_generator \n"; 211 | cerr << " Where matches the " CFGKEY_SERVER_URL 212 | " Harmony configuration key.\n"; 213 | return -1; 214 | } 215 | 216 | if (file_type(argv[1]) != 2) { 217 | cerr << argv[1] << " is not a valid directory. Exiting.\n"; 218 | return -1; 219 | } 220 | 221 | local_url.path = argv[1]; 222 | local_url.host.clear(); 223 | 224 | // Main loop starts here. 225 | 226 | // Update the log file. 227 | if (dir_erase(local_url.path) < 0) { 228 | cerr << "Could not prepare local directory for incoming messages.\n"; 229 | return -1; 230 | } 231 | 232 | string init_filename = local_url.path + "/" + infile_name + ".-1"; 233 | string next_filename; 234 | while (true) { 235 | // Construct the next timestep filename. 236 | ss.str(""); 237 | ss << local_url.path << "/" << infile_name << "." << timestep; 238 | next_filename = ss.str(); 239 | 240 | cout << "Waiting to hear from harmony server..." << endl; 241 | log_message << "Waiting to hear from harmony server...\n"; 242 | while (!file_type(init_filename.c_str()) && 243 | !file_type(next_filename.c_str())) 244 | { 245 | // Quick check to see if any slaves have completed. 246 | while ( (pid = waitpid(-1, &status, WNOHANG)) > 0) { 247 | if (slave_complete(pid) == 0) 248 | ++num_ready; 249 | } 250 | sleep(1); 251 | } 252 | 253 | if (file_type(init_filename.c_str())) { 254 | cout << "Harmony initialization file found." << endl; 255 | if (codeserver_init(init_filename) < 0) { 256 | cerr << "Removing invalid configuration file.\n"; 257 | } 258 | else { 259 | // Record some data? How many variants produced in total? 260 | timestep = 0; 261 | num_ready = gen_list.size(); 262 | cout << "Beginning new code server session." << endl; 263 | } 264 | remove(init_filename.c_str()); 265 | continue; 266 | } 267 | 268 | cout << "Filename: " << next_filename << endl; 269 | 270 | double time1__, time2__; 271 | time1__=time_stamp(); 272 | 273 | // Find an available generator slot. 274 | for (i = 0; i < gen_list.size(); ++i) { 275 | if (gen_list[i].pid == 0) { 276 | mesg_read(next_filename.c_str(), &gen_list[i].mesg); 277 | gen_list[i].step = timestep; 278 | generator_make(gen_list[i]); 279 | --num_ready; 280 | break; 281 | } 282 | } 283 | 284 | if (i == gen_list.size()) 285 | assert(0 && "Generator vector overflow"); 286 | 287 | if (num_ready == 0) { 288 | // All slaves are busy. Sit and wait until one returns. 289 | pid = waitpid(-1, &status, 0); 290 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 291 | cerr << "Process " << i 292 | << " (pid " << pid << ") failed.\n"; 293 | exit(1); 294 | } 295 | if (slave_complete(pid) == 0) 296 | ++num_ready; 297 | } 298 | 299 | time2__=time_stamp(); 300 | double elapsed__=time2__-time1__; 301 | log_message << "Total time for iteration "<< timestep 302 | << " : " << elapsed__ << "\n------------------\n"; 303 | logger(log_message.str()); 304 | log_message.str(""); 305 | 306 | // Remove the conf file we just processed. 307 | std::remove(next_filename.c_str()); 308 | cout << "Iteration complete." << endl; 309 | 310 | // Increment the timestep. 311 | timestep++; 312 | } // Main loop. 313 | } 314 | 315 | int codeserver_init(string& filename) 316 | { 317 | const char* cfgval; 318 | hmesg_t init_mesg; 319 | stringstream ss; 320 | 321 | if (mesg_read(filename.c_str(), &init_mesg) < 0) { 322 | fprintf(stderr, "Could not parse initial message.\n"); 323 | return -1; 324 | } 325 | std::remove(filename.c_str()); 326 | 327 | if (dir_erase(local_url.path) < 0) { 328 | cerr << "Could not clear incoming directory.\n"; 329 | return -1; 330 | } 331 | 332 | appname = init_mesg.state.space->name; 333 | 334 | cfgval = hcfg_get(init_mesg.data.cfg, CFGKEY_SERVER_URL); 335 | if (!cfgval) { 336 | cerr << "Session does not define local URL.\n"; 337 | return -1; 338 | } 339 | if (url_parse(cfgval, local_url) < 0) { 340 | cerr << "Invalid local URL: '" << cfgval << "'\n"; 341 | return -1; 342 | } 343 | 344 | cfgval = hcfg_get(init_mesg.data.cfg, CFGKEY_TARGET_URL); 345 | if (!cfgval) { 346 | cerr << "Session does not define target URL.\n"; 347 | return -1; 348 | } 349 | if (url_parse(cfgval, target_url) < 0) { 350 | cerr << "Invalid target URL: '" << cfgval << "'\n"; 351 | return -1; 352 | } 353 | 354 | cfgval = hcfg_get(init_mesg.data.cfg, CFGKEY_REPLY_URL); 355 | if (!cfgval) { 356 | cerr << "Session does not define reply URL.\n"; 357 | return -1; 358 | } 359 | if (url_parse(cfgval, reply_url) < 0) { 360 | cerr << "Invalid reply URL: '" << cfgval << "'\n"; 361 | return -1; 362 | } 363 | 364 | cfgval = hcfg_get(init_mesg.data.cfg, CFGKEY_SLAVE_LIST); 365 | if (!cfgval) { 366 | cerr << "Session does not define slave list.\n"; 367 | return -1; 368 | } 369 | if (parse_slave_list(cfgval) < 0) { 370 | cerr << "Error: codegen_slave_list config directive invalid.\n" 371 | << "Please fix the harmony server's global config file.\n"; 372 | return -1; 373 | } 374 | 375 | cfgval = hcfg_get(init_mesg.data.cfg, CFGKEY_SLAVE_PATH); 376 | if (!cfgval) { 377 | cerr << "Session does not define slave directory.\n"; 378 | return -1; 379 | } 380 | slave_path = cfgval; 381 | 382 | // Initialize the application log file. 383 | ss.str(""); 384 | ss << "generation." << appname << ".log"; 385 | log_file = ss.str(); 386 | cout << "Generating code for: " << appname << endl; 387 | 388 | cout << "The list of available machines:" << endl; 389 | log_message << "-------------------------------------------\n"; 390 | log_message << "The list of available machines: "; 391 | 392 | for (unsigned i = 0; i < gen_list.size(); ++i) 393 | { 394 | cout << gen_list[i].hostname << " "; 395 | log_message << gen_list[i].hostname << " "; 396 | } 397 | cout << "\n"; 398 | log_message << "\n"; 399 | 400 | logger(log_message.str()); 401 | log_message.str(""); 402 | 403 | // Run the setup_code_gen_hosts.sh script. 404 | ss.str(""); 405 | ss << "/bin/sh setup_code_gen_hosts.sh " << appname << " " 406 | << slave_path << " " << local_url.host; 407 | for (unsigned i = 0; i < gen_list.size(); ++i) { 408 | ss << " " << gen_list[i].hostname; 409 | } 410 | if (system(ss.str().c_str()) != 0) { 411 | cout << "Error on system(" << ss.str() << ")" << endl; 412 | return -1; 413 | } 414 | 415 | // Respond to the Harmony server. 416 | init_mesg.status = HMESG_STATUS_OK; 417 | if (mesg_write(init_mesg, -1) < 0) { 418 | cerr << "Could not write/send initial reply message.\n"; 419 | return -1; 420 | } 421 | 422 | cout << "Session initialized. Ready to generate code.\n"; 423 | return 0; 424 | } 425 | 426 | /* 427 | * Internal helper function implementation. 428 | */ 429 | double time_stamp(void) 430 | { 431 | struct timeval t; 432 | double time; 433 | gettimeofday(&t, NULL); 434 | time = t.tv_sec + 1.0e-6*t.tv_usec; 435 | return time; 436 | } 437 | 438 | /* 439 | * This function only parses out hosts and paths. A more 440 | * sophisticated version will be required when the codeserver is 441 | * overhauled. 442 | */ 443 | int url_parse(const char* str, url_t& url) 444 | { 445 | const char* ptr; 446 | 447 | ptr = strstr(str, "//"); 448 | if (!ptr) 449 | return -1; 450 | ptr += 2; 451 | 452 | if (strncmp("dir://", str, ptr - str) == 0) { 453 | url.path = ptr; 454 | url.host.clear(); 455 | return 0; 456 | } 457 | else if (strncmp("ssh://", str, ptr - str) == 0) { 458 | str = ptr; 459 | ptr = strchr(str, '@'); 460 | if (ptr) { 461 | url.user = string(str, ptr - str); 462 | str = ptr + 1; 463 | } 464 | else { 465 | url.user.clear(); 466 | } 467 | 468 | ptr = strchr(str, ':'); 469 | if (ptr) { 470 | url.host = string(str, ptr - str); 471 | } 472 | else { 473 | url.host.clear(); 474 | url.port.clear(); 475 | } 476 | 477 | ptr = strchr(str, '/'); 478 | if (!ptr) { 479 | cerr << "Error parsing URL: No path separator.\n"; 480 | return -1; 481 | } 482 | 483 | if (url.host.empty()) 484 | url.host = string(str, ptr - str); 485 | else 486 | url.port = string(str, ptr - str); 487 | 488 | url.path = ++ptr; 489 | return 0; 490 | } 491 | else if (strncmp("tcp://", str, ptr - str) == 0) { 492 | // Not implemented yet. 493 | } 494 | return -1; 495 | } 496 | 497 | int dir_erase(string& dirname) 498 | { 499 | DIR* dirfd; 500 | struct dirent* dent; 501 | stringstream initfile; 502 | 503 | dirfd = opendir(dirname.c_str()); 504 | if (!dirfd) 505 | return -1; 506 | 507 | initfile << infile_name << ".-1"; 508 | while ( (dent = readdir(dirfd))) { 509 | if (initfile.str() == dent->d_name) 510 | continue; // Do not delete an initial file, if found. 511 | 512 | if (strncmp(dent->d_name, infile_name, strlen(infile_name)) == 0) { 513 | string fullpath = dirname + "/" + dent->d_name; 514 | std::remove(fullpath.c_str()); 515 | } 516 | } 517 | 518 | if (closedir(dirfd) < 0) 519 | return -1; 520 | 521 | return 0; 522 | } 523 | 524 | void logger(const string& message) 525 | { 526 | string line; 527 | ofstream out_file; 528 | out_file.open(log_file.c_str(),ios::app); 529 | if(!out_file) 530 | { 531 | cerr << "Error file could not be opened \n"; 532 | exit(1); 533 | } 534 | 535 | out_file << message; 536 | out_file.flush(); 537 | out_file.close(); 538 | } 539 | 540 | int parse_slave_list(const char* hostlist) 541 | { 542 | const char* end; 543 | const char* head; 544 | const char* tail; 545 | const char* host_ptr; 546 | char* num_ptr; 547 | string host; 548 | long num; 549 | stringstream ss; 550 | generator_t newgen; 551 | 552 | if (hostlist == NULL) 553 | return -1; 554 | 555 | while (gen_list.size()) { 556 | if (gen_list.back().pid) 557 | hmesg_fini(&gen_list.back().mesg); 558 | gen_list.pop_back(); 559 | } 560 | 561 | newgen.pid = 0; 562 | newgen.mesg = hmesg_zero; 563 | 564 | end = hostlist + strlen(hostlist); 565 | head = hostlist; 566 | while (head < end) { 567 | host = ""; 568 | num = -1; 569 | 570 | // Find the entry boundary. 571 | tail = reinterpret_cast(memchr(head, ',', end - head)); 572 | if (!tail) { 573 | tail = end; 574 | } 575 | 576 | // Skip leading whitespace. 577 | while (head < tail && (*head == '\0' || isspace(*head))) { 578 | ++head; 579 | } 580 | host_ptr = head; 581 | 582 | // Find host boundary whitespace. 583 | while (head < tail && (*head != '\0' && !isspace(*head))) { 584 | ++head; 585 | } 586 | host = string(host_ptr, head++); 587 | 588 | // Find the unsigned integer after the host. 589 | errno = 0; 590 | num = strtol(head, &num_ptr, 0); 591 | if (errno != 0) { 592 | num = -1; 593 | head = tail; 594 | } else { 595 | head = num_ptr; 596 | } 597 | 598 | // Skip trailing whitespace. 599 | while (head < tail && (*head == '\0' || isspace(*head))) { 600 | ++head; 601 | } 602 | 603 | // Error check. 604 | if (host.empty() || num == -1 || head != tail) { 605 | cerr << " values_of(const hpoint_t* pt) 646 | { 647 | vector retval; 648 | 649 | for (int i = 0; i < pt->len; ++i) { 650 | if (pt->term[i].type != HVAL_INT) { 651 | cerr << "Codeserver only implemented for int ranges for now.\n"; 652 | retval.clear(); 653 | break; 654 | } 655 | retval.push_back(pt->term[i].value.i); 656 | } 657 | return retval; 658 | } 659 | 660 | string vector_to_string(vector& v) 661 | { 662 | stringstream ss; 663 | ss << " "; 664 | for (unsigned i = 0; i < v.size(); i++) 665 | ss << v[i] << " "; 666 | 667 | return ss.str(); 668 | } 669 | 670 | string vector_to_bash_array_remote(vector& v) 671 | { 672 | stringstream ss; 673 | ss << "\\\""; 674 | for (unsigned i = 0; i < v.size(); i++) 675 | { 676 | ss << v[i] << " "; 677 | } 678 | ss << "\\\" "; 679 | return ss.str(); 680 | } 681 | 682 | string vector_to_bash_array_local(vector& v) 683 | { 684 | stringstream ss; 685 | ss << "\""; 686 | for (unsigned i = 0; i < v.size(); i++) 687 | { 688 | ss << v[i] << " "; 689 | } 690 | ss << "\" "; 691 | return ss.str(); 692 | } 693 | 694 | int file_type(const char* fileName) 695 | { 696 | struct stat buf; 697 | if (fileName == NULL) { 698 | return 0; 699 | } 700 | 701 | int i = stat ( fileName, &buf ); 702 | if (i != 0) { 703 | return 0; 704 | 705 | } else if (S_ISREG(buf.st_mode) && buf.st_size > 0) { 706 | return 1; 707 | 708 | } else if (S_ISDIR(buf.st_mode)) { 709 | return 2; 710 | } 711 | return 0; 712 | } 713 | 714 | #define POLL_TIME 250000 715 | 716 | int mesg_read(const char* filename, hmesg_t* msg) 717 | { 718 | int msglen, fd, retries, retval; 719 | struct stat sb; 720 | struct timeval polltime; 721 | 722 | retval = 0; 723 | retries = 3; 724 | 725 | top: 726 | fd = open(filename, O_RDONLY); 727 | if (fd == -1) { 728 | cerr << "Could not open file: " << strerror(errno) << ".\n"; 729 | retval = -1; 730 | goto cleanup; 731 | } 732 | 733 | // Obtain file size. 734 | if (fstat(fd, &sb) != 0) { 735 | cerr << "Could not fstat file: " << strerror(errno) << ". Retrying.\n"; 736 | goto retry; 737 | } 738 | 739 | msglen = sb.st_size + 1; 740 | if (msg->recv_len < msglen) { 741 | char* newbuf = reinterpret_cast(realloc(msg->recv_buf, msglen)); 742 | if (!newbuf) { 743 | cerr << "Could not allocate memory for message data.\n"; 744 | retval = -1; 745 | goto cleanup; 746 | } 747 | msg->recv_buf = newbuf; 748 | msg->recv_len = msglen; 749 | } 750 | msg->recv_buf[sb.st_size] = '\0'; 751 | 752 | if (read_loop(fd, msg->recv_buf, sb.st_size) != 0) { 753 | cerr << "Error reading message file. Retrying.\n"; 754 | goto retry; 755 | } 756 | 757 | if (close(fd) < 0) { 758 | cerr << "Error closing code completion message file.\n"; 759 | retval = -1; 760 | goto cleanup; 761 | } 762 | fd = -1; 763 | 764 | if (hmesg_unpack(msg) < 0) { 765 | cerr << "Error decoding message file. Retrying.\n"; 766 | goto retry; 767 | } 768 | retval = 1; 769 | 770 | cleanup: 771 | if (fd >= 0) 772 | close(fd); 773 | return retval; 774 | 775 | retry: 776 | // Avoid the race condition where we attempt to read a message 777 | // before the remote code server scp process has completely 778 | // written the file. 779 | // 780 | // At some point, this retry method should probably be replaced 781 | // with file locks as a more complete solution. 782 | // 783 | close(fd); 784 | if (--retries) { 785 | polltime.tv_sec = 0; 786 | polltime.tv_usec = POLL_TIME; 787 | select(0, NULL, NULL, NULL, &polltime); 788 | goto top; 789 | } 790 | return -1; 791 | } 792 | 793 | int mesg_write(hmesg_t& mesg, int step) 794 | { 795 | stringstream ss; 796 | string filename; 797 | int msglen, fd; 798 | 799 | ss << local_url.path << "/" << outfile_name << "." << step; 800 | filename = ss.str(); 801 | fd = open(filename.c_str(), O_WRONLY | O_CREAT, 0666); 802 | if (!fd) 803 | return -1; 804 | 805 | mesg.status = HMESG_STATUS_OK; 806 | msglen = hmesg_pack(&mesg); 807 | if (msglen < 0) { 808 | cerr << "Error encoding message file.\n"; 809 | return -1; 810 | } 811 | 812 | if (write_loop(fd, mesg.send_buf, msglen) < 0) 813 | return -1; 814 | 815 | if (close(fd) < 0) 816 | return -1; 817 | 818 | if (!reply_url.host.empty()) { 819 | // Call scp to transfer the file. 820 | ss.str(""); 821 | ss << "scp "; 822 | 823 | if (!reply_url.port.empty()) 824 | ss << "-P " << reply_url.port; 825 | 826 | ss << filename << " "; 827 | 828 | if (!reply_url.user.empty()) 829 | ss << reply_url.user << "@"; 830 | 831 | ss << reply_url.host << ":" << reply_url.path; 832 | 833 | if (system(ss.str().c_str()) == -1) { 834 | cerr << "Error calling scp to transfer message.\n"; 835 | return -1; 836 | } 837 | std::remove(filename.c_str()); 838 | } 839 | return 0; 840 | } 841 | 842 | int read_loop(int fd, char* buf, int len) 843 | { 844 | int count; 845 | 846 | while (len > 0) { 847 | count = read(fd, buf, len); 848 | if (count < 0 && errno == EINTR) 849 | continue; 850 | 851 | if (count <= 0) 852 | return -1; 853 | 854 | buf += count; 855 | len -= count; 856 | } 857 | return 0; 858 | } 859 | 860 | int write_loop(int fd, char* buf, int len) 861 | { 862 | int count; 863 | 864 | while (len > 0) { 865 | count = write(fd, buf, len); 866 | if (count < 0 && errno == EINTR) 867 | continue; 868 | 869 | if (count <= 0) 870 | return -1; 871 | 872 | buf += count; 873 | len -= count; 874 | } 875 | return 0; 876 | } 877 | --------------------------------------------------------------------------------