NEURON
nrnmpi_dynam.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 #include "nrnmpiuse.h"
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <assert.h>
8 
9 #if NRNMPI_DYNAMICLOAD /* to end of file */
10 
11 #ifdef MINGW
12 #define RTLD_NOW 0
13 #define RTLD_GLOBAL 0
14 #define RTLD_NOLOAD 0
15 extern "C" {
16 extern void* dlopen_noerr(const char* name, int mode);
17 #define dlopen dlopen_noerr
18 extern void* dlsym(void* handle, const char* name);
19 extern int dlclose(void* handle);
20 extern char* dlerror();
21 }
22 #else
23 #include <dlfcn.h>
24 #endif
25 
26 #include "nrnmpi.h"
27 
28 extern char* cxx_char_alloc(size_t);
29 extern std::string corenrn_mpi_library;
30 
31 #if DARWIN
32 extern void nrn_possible_mismatched_arch(const char*);
33 #endif
34 
35 #if DARWIN || defined(__linux__)
36 extern const char* path_prefix_to_libnrniv();
37 #endif
38 
39 #include <cstddef>
40 
41 #include "mpispike.h"
42 #include "nrnmpi_def_cinc" /* nrnmpi global variables */
43 extern "C" {
44 #include "nrnmpi_dynam_cinc" /* autogenerated file */
45 }
46 #include "nrnmpi_dynam_wrappers.inc" /* autogenerated file */
47 #include "nrnmpi_dynam_stubs.cpp"
48 
49 static void* load_mpi(const char* name, char* mes) {
50  int flag = RTLD_NOW | RTLD_GLOBAL;
51  void* handle = dlopen(name, flag);
52  if (!handle) {
53 #if DARWIN
54  nrn_possible_mismatched_arch(name);
55 #endif
56  sprintf(mes, "load_mpi: %s\n", dlerror());
57  }else{
58  sprintf(mes, "load_mpi: %s successful\n", name);
59  }
60  return handle;
61 }
62 
63 static void* load_nrnmpi(const char* name, char* mes) {
64  int i;
65  int flag = RTLD_NOW | RTLD_GLOBAL;
66  void* handle = dlopen(name, flag);
67  if (!handle) {
68  sprintf(mes, "load_nrnmpi: %s\n", dlerror());
69  return 0;
70  }
71  sprintf(mes, "load_nrnmpi: %s successful\n", name);
72  for (i = 0; ftable[i].name; ++i) {
73  void* p = dlsym(handle, ftable[i].name);
74  if (!p) {
75  sprintf(mes+strlen(mes), "load_nrnmpi: %s\n", dlerror());
76  return 0;
77  }
78  *ftable[i].ppf = p;
79  }
80  {
81  char* (**p)(size_t) = (char* (**)(size_t))dlsym(handle, "p_cxx_char_alloc");
82  if (!p) {
83  sprintf(mes+strlen(mes), "load_nrnmpi: %s\n", dlerror());
84  return 0;
85  }
86  *p = cxx_char_alloc;
87  }
88  return handle;
89 }
90 
91 char* nrnmpi_load(int is_python) {
92  int ismes=0;
93  char* pmes;
94  void* handle = NULL;
95  pmes = static_cast<char*>(malloc(4096));
96  assert(pmes);
97  pmes[0]='\0';
98 #if DARWIN
99  sprintf(pmes, "Try loading libmpi\n");
100  handle = load_mpi("libmpi.dylib", pmes+strlen(pmes));
101  /**
102  * If libmpi.dylib is not in the standard location and dlopen fails
103  * then try to use user provided or ctypes.find_library() provided
104  * mpi library path.
105  */
106  if(!handle) {
107  const char* mpi_lib_path = getenv("MPI_LIB_NRN_PATH");
108  if (mpi_lib_path) {
109  handle = load_mpi(mpi_lib_path, pmes+strlen(pmes));
110  if (!handle) {
111  sprintf(pmes, "Can not load libmpi.dylib and %s\n", mpi_lib_path);
112  }
113  }
114  }
115  if (handle) {
116  /* loaded but is it openmpi or mpich */
117  if (dlsym(handle, "ompi_mpi_init")) { /* it is openmpi */
118  /* see man dyld */
119  if (!load_nrnmpi("@loader_path/libnrnmpi_ompi.dylib", pmes+strlen(pmes))) {
120  return pmes;
121  }
122  corenrn_mpi_library = "@loader_path/libcorenrnmpi_ompi.dylib";
123  }else{ /* must be mpich. Could check for MPID_nem_mpich_init...*/
124  if (!load_nrnmpi("@loader_path/libnrnmpi_mpich.dylib", pmes+strlen(pmes))) {
125  return pmes;
126  }
127  corenrn_mpi_library = "@loader_path/libcorenrnmpi_mpich.dylib";
128  }
129  }else{
130  ismes = 1;
131 sprintf(pmes+strlen(pmes), "Is openmpi or mpich installed? If not in default location, "
132  "need a LD_LIBRARY_PATH on Linux or DYLD_LIBRARY_PATH on Mac OS. "
133  "On Mac OS, full path to a MPI library can be provided via "
134  "environmental variable MPI_LIB_NRN_PATH\n");
135  }
136 #else /*not DARWIN*/
137 #if defined(MINGW)
138  sprintf(pmes, "Try loading msmpi\n");
139  handle = load_mpi("msmpi.dll", pmes+strlen(pmes));
140  if (handle) {
141  if (!load_nrnmpi("libnrnmpi_msmpi.dll", pmes+strlen(pmes))){
142  return pmes;
143  }
144  corenrn_mpi_library = "libcorenrnmpi_msmpi.dll";
145  }else{
146  ismes = 1;
147  return pmes;
148  }
149 #else /*not MINGW so must be __linux__*/
150 
151  /**
152  * libmpi.so is not standard but used by most of the implemenntation
153  * (mpich, openmpi, intel-mpi, parastation-mpi, hpe-mpt) but not cray-mpich.
154  * we first load libmpi and then libmpich.so as a fallaback for cray system.
155  */
156  sprintf(pmes, "Try loading libmpi\n");
157  handle = load_mpi("libmpi.so", pmes+strlen(pmes));
158 
159  // like osx, check if user has provided library via MPI_LIB_NRN_PATH
160  if(!handle) {
161  const char* mpi_lib_path = getenv("MPI_LIB_NRN_PATH");
162  if (mpi_lib_path) {
163  handle = load_mpi(mpi_lib_path, pmes+strlen(pmes));
164  if (!handle) {
165  sprintf(pmes, "Can not load libmpi.so and %s", mpi_lib_path);
166  }
167  }
168  }
169 
170  if (!handle) {
171  sprintf(pmes, "Try loading libmpi and libmpich\n");
172  handle = load_mpi("libmpich.so", pmes+strlen(pmes));
173  }
174 
175  if (handle) {
176  /* with CMAKE the problem of Python launch on LINUX not resolving
177  variables from already loaded shared libraries has returned.
178  */
179  {
180  std::string error{"Promoted none of"};
181  auto const promote_to_global = [&error](const char* lib) {
182  if(!dlopen(lib, RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL)) {
183  char const* dlerr = dlerror();
184  error = error + ' ' + lib + " (" + (dlerr ? dlerr : "nullptr") + ')';
185  return false;
186  }
187  return true;
188  };
189  if(!promote_to_global("libnrniv.so") && !promote_to_global("libnrniv-without-nvidia.so")) {
190  std::cerr << error << " to RTLD_GLOBAL" << std::endl;
191  }
192  }
193  // Figure out where to find lib[core]nrnmpi{...} libraries.
194  auto const libnrnmpi_prefix = []() -> std::string {
195  if(const char* nrn_home = std::getenv("NRNHOME")) {
196  // TODO: what about windows path separators?
197  return std::string{nrn_home} + "/lib/";
198  } else {
199  // Use the directory libnrniv.so is in
200  return path_prefix_to_libnrniv();
201  }
202  }();
203  // `handle` refers to "libmpi.so", figure out which MPI implementation that
204  // is.
205  auto const mpi_implementation = [handle] {
206  if (dlsym(handle, "ompi_mpi_init")) {
207  // OpenMPI
208  return "ompi";
209  } else if (dlsym(handle, "MPI_SGI_vtune_is_running")) {
210  // Got sgi-mpt. MPI_SGI_init exists in both mpt and hmpt, so we look
211  // for MPI_SGI_vtune_is_running which only exists in the non-hmpt
212  // version.
213  return "mpt";
214  } else {
215  // Assume mpich. Could check for MPID_nem_mpich_init...
216  return "mpich";
217  }
218  }();
219  auto const nrn_mpi_library = libnrnmpi_prefix + "libnrnmpi_" + mpi_implementation + ".so";
220  corenrn_mpi_library = libnrnmpi_prefix + "libcorenrnmpi_" + mpi_implementation + ".so";
221  if (!load_nrnmpi(nrn_mpi_library.c_str(), pmes + strlen(pmes))) {
222  return pmes;
223  }
224  } else {
225  ismes = 1;
226 sprintf(pmes+strlen(pmes), "Is openmpi, mpich, intel-mpi, sgi-mpt etc. installed? If not in default location, need a LD_LIBRARY_PATH or MPI_LIB_NRN_PATH.\n");
227  }
228 #endif /*not MINGW*/
229 #endif /* not DARWIN */
230  if (!handle) {
231  sprintf(pmes+strlen(pmes), "could not dynamically load libmpi.so or libmpich.so\n");
232  return pmes;
233  }
234  free(pmes);
235  return 0;
236 }
237 #endif
#define assert(ex)
Definition: hocassrt.h:26
if(status)
const char * dlerror(void)
Definition: osxdlfcn.cpp:174
size_t p
sprintf(buf," if (secondorder) {\ " int _i;\" " for(_i=0;_i< %d;++_i) {\" " _p[_slist%d[_i]]+=dt *_p[_dlist%d[_i]];\" " }}\", numeqn, listnum, listnum)
int dlclose(void *handle)
Definition: osxdlfcn.cpp:178
#define RTLD_NOW
Definition: osxdlfcn.h:61
char * getenv(const char *s)
Definition: macprt.cpp:67
void * dlopen(const char *path, int mode)
Definition: osxdlfcn.cpp:85
char * name
Definition: init.cpp:16
std::string corenrn_mpi_library
char * cxx_char_alloc(size_t sz)
Definition: ivoc.cpp:122
#define RTLD_NOLOAD
Definition: osxdlfcn.h:64
#define i
Definition: md1redef.h:12
#define error(err_num, fn_name)
Definition: err.h:73
void * dlsym(void *handle, const char *symbol)
Definition: osxdlfcn.cpp:193
return NULL
Definition: cabcode.cpp:461
#define RTLD_GLOBAL
Definition: osxdlfcn.h:63