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