935a6d553add1c3cf2119ae6dfd08ac59cdc1368
[alexxy/gromacs.git] / src / gromacs / utility / binaryinformation.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
7  * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
8  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9  * and including many others, as listed in the AUTHORS file in the
10  * top-level source directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 /*! \internal \file
39  * \brief Implements functionality for printing information about the
40  * currently running binary
41  *
42  * \ingroup module_utility
43  */
44 #include "gmxpre.h"
45
46 #include "binaryinformation.h"
47
48 #include "config.h"
49
50 #if GMX_FFT_FFTW3 || GMX_FFT_ARMPL_FFTW3
51 // Needed for construction of the FFT library description string
52 #    include <fftw3.h>
53 #endif
54
55 #ifdef HAVE_LIBMKL
56 #    include <mkl.h>
57 #endif
58
59 #if HAVE_EXTRAE
60 #    include <extrae_user_events.h>
61 #endif
62
63 #if GMX_USE_HWLOC
64 #    include <hwloc.h>
65 #endif
66
67 #include <cstdio>
68 #include <cstdlib>
69 #include <cstring>
70
71 #include <algorithm>
72 #include <array>
73 #include <string>
74
75 /* This file is completely threadsafe - keep it that way! */
76
77 #include "buildinfo.h"
78 #include "gromacs/utility/arraysize.h"
79 #include "gromacs/utility/baseversion.h"
80 #include "gromacs/utility/exceptions.h"
81 #include "gromacs/utility/gmxassert.h"
82 #include "gromacs/utility/path.h"
83 #include "gromacs/utility/programcontext.h"
84 #include "gromacs/utility/stringutil.h"
85 #include "gromacs/utility/sysinfo.h"
86 #include "gromacs/utility/textwriter.h"
87
88 #include "cuda_version_information.h"
89
90 namespace
91 {
92
93 using gmx::formatString;
94
95 //! \cond Doxygen does not need to care about most of this stuff, and the macro usage is painful to document
96
97 int centeringOffset(int width, int length)
98 {
99     return std::max(width - length, 0) / 2;
100 }
101
102 std::string formatCentered(int width, const char* text)
103 {
104     const int offset = centeringOffset(width, std::strlen(text));
105     return formatString("%*s%s", offset, "", text);
106 }
107
108 void printCopyright(gmx::TextWriter* writer)
109 {
110     // Contributors sorted alphabetically by last name
111     static const char* const Contributors[]  = { "Andrey Alekseenko",
112                                                 "Emile Apol",
113                                                 "Rossen Apostolov",
114                                                 "Paul Bauer",
115                                                 "Herman J.C. Berendsen",
116                                                 "Par Bjelkmar",
117                                                 "Christian Blau",
118                                                 "Viacheslav Bolnykh",
119                                                 "Kevin Boyd",
120                                                 "Aldert van Buuren",
121                                                 "Rudi van Drunen",
122                                                 "Anton Feenstra",
123                                                 "Gaurav Garg",
124                                                 "Gilles Gouaillardet",
125                                                 "Alan Gray",
126                                                 "Gerrit Groenhof",
127                                                 "Anca Hamuraru",
128                                                 "Vincent Hindriksen",
129                                                 "M. Eric Irrgang",
130                                                 "Aleksei Iupinov",
131                                                 "Christoph Junghans",
132                                                 "Joe Jordan",
133                                                 "Dimitrios Karkoulis",
134                                                 "Peter Kasson",
135                                                 "Jiri Kraus",
136                                                 "Carsten Kutzner",
137                                                 "Per Larsson",
138                                                 "Justin A. Lemkul",
139                                                 "Viveca Lindahl",
140                                                 "Magnus Lundborg",
141                                                 "Erik Marklund",
142                                                 "Pascal Merz",
143                                                 "Pieter Meulenhoff",
144                                                 "Teemu Murtola",
145                                                 "Szilard Pall",
146                                                 "Sander Pronk",
147                                                 "Roland Schulz",
148                                                 "Michael Shirts",
149                                                 "Alexey Shvetsov",
150                                                 "Alfons Sijbers",
151                                                 "Peter Tieleman",
152                                                 "Jon Vincent",
153                                                 "Teemu Virolainen",
154                                                 "Christian Wennberg",
155                                                 "Maarten Wolf",
156                                                 "Artem Zhmurov" };
157     static const char* const CopyrightText[] = {
158         "Copyright (c) 1991-2000, University of Groningen, The Netherlands.",
159         "Copyright (c) 2001-2019, The GROMACS development team at",
160         "Uppsala University, Stockholm University and",
161         "the Royal Institute of Technology, Sweden.",
162         "check out http://www.gromacs.org for more information."
163     };
164
165 #define NCONTRIBUTORS static_cast<int>(asize(Contributors))
166 #define NCR static_cast<int>(asize(CopyrightText))
167
168     // TODO a centering behaviour of TextWriter could be useful here
169     writer->writeLine(formatCentered(78, "GROMACS is written by:"));
170     for (int i = 0; i < NCONTRIBUTORS;)
171     {
172         for (int j = 0; j < 3 && i < NCONTRIBUTORS; ++j, ++i)
173         {
174             const int            width = 26;
175             std::array<char, 30> buf;
176             const int            offset = centeringOffset(width, strlen(Contributors[i]));
177             GMX_RELEASE_ASSERT(static_cast<int>(strlen(Contributors[i])) + offset < gmx::ssize(buf),
178                                "Formatting buffer is not long enough");
179             std::fill(buf.begin(), buf.begin() + offset, ' ');
180             std::strncpy(buf.data() + offset, Contributors[i], gmx::ssize(buf) - offset);
181             writer->writeString(formatString(" %-*s", width, buf.data()));
182         }
183         writer->ensureLineBreak();
184     }
185     writer->writeLine(formatCentered(78, "and the project leaders:"));
186     writer->writeLine(
187             formatCentered(78, "Mark Abraham, Berk Hess, Erik Lindahl, and David van der Spoel"));
188     writer->ensureEmptyLine();
189     for (int i = 0; i < NCR; ++i)
190     {
191         writer->writeLine(CopyrightText[i]);
192     }
193     writer->ensureEmptyLine();
194
195     // Folding At Home has different licence to allow digital
196     // signatures in GROMACS, so does not need to show the normal
197     // license statement.
198     if (!GMX_FAHCORE)
199     {
200         writer->writeLine("GROMACS is free software; you can redistribute it and/or modify it");
201         writer->writeLine("under the terms of the GNU Lesser General Public License");
202         writer->writeLine("as published by the Free Software Foundation; either version 2.1");
203         writer->writeLine("of the License, or (at your option) any later version.");
204     }
205 }
206
207 // Construct a string that describes the library that provides FFT support to this build
208 const char* getFftDescriptionString()
209 {
210 // Define the FFT description string
211 #if GMX_FFT_FFTW3 || GMX_FFT_ARMPL_FFTW3
212 #    if GMX_NATIVE_WINDOWS
213     // Don't buy trouble
214     return "fftw3";
215 #    else
216     // Use the version string provided by libfftw3
217 #        if GMX_DOUBLE
218     return fftw_version;
219 #        else
220     return fftwf_version;
221 #        endif
222 #    endif
223 #endif
224 #if GMX_FFT_MKL
225     return "Intel MKL";
226 #endif
227 #if GMX_FFT_FFTPACK
228     return "fftpack (built-in)";
229 #endif
230 };
231
232 void gmx_print_version_info(gmx::TextWriter* writer)
233 {
234     writer->writeLine(formatString("GROMACS version:    %s", gmx_version()));
235     const char* const git_hash = gmx_version_git_full_hash();
236     if (git_hash[0] != '\0')
237     {
238         writer->writeLine(formatString("GIT SHA1 hash:      %s", git_hash));
239     }
240     const char* const base_hash = gmx_version_git_central_base_hash();
241     if (base_hash[0] != '\0')
242     {
243         writer->writeLine(formatString("Branched from:      %s", base_hash));
244     }
245     const char* const releaseSourceChecksum = gmxReleaseSourceChecksum();
246     const char* const currentSourceChecksum = gmxCurrentSourceChecksum();
247     if (releaseSourceChecksum[0] != '\0')
248     {
249         if (std::strcmp(releaseSourceChecksum, "NoChecksumFile") == 0)
250         {
251             writer->writeLine(formatString(
252                     "The source code this program was compiled from has not been verified because "
253                     "the reference checksum was missing during compilation. This means you have an "
254                     "incomplete GROMACS distribution, please make sure to download an intact "
255                     "source distribution and compile that before proceeding."));
256             writer->writeLine(formatString("Computed checksum: %s", currentSourceChecksum));
257         }
258         else if (std::strcmp(releaseSourceChecksum, "NoPythonAvailable") == 0)
259         {
260             writer->writeLine(
261                     formatString("Build source could not be verified, because the checksum could "
262                                  "not be computed."));
263         }
264         else if (std::strcmp(releaseSourceChecksum, currentSourceChecksum) != 0)
265         {
266             writer->writeLine(formatString(
267                     "This program has been built from source code that has been altered and does "
268                     "not match the code released as part of the official GROMACS version %s. If "
269                     "you did not intend to use an altered GROMACS version, make sure to download "
270                     "an intact source distribution and compile that before proceeding.",
271                     gmx_version()));
272             writer->writeLine(formatString(
273                     "If you have modified the source code, you are strongly encouraged to set your "
274                     "custom version suffix (using -DGMX_VERSION_STRING_OF_FORK) which will can "
275                     "help later with scientific reproducibility but also when reporting bugs."));
276             writer->writeLine(formatString("Release checksum: %s", releaseSourceChecksum));
277             writer->writeLine(formatString("Computed checksum: %s", currentSourceChecksum));
278         }
279         else
280         {
281             writer->writeLine(formatString("Verified release checksum is %s", releaseSourceChecksum));
282         }
283     }
284
285
286 #if GMX_DOUBLE
287     writer->writeLine("Precision:          double");
288 #else
289     writer->writeLine("Precision:          mixed");
290 #endif
291     writer->writeLine(formatString("Memory model:       %u bit", static_cast<unsigned>(8 * sizeof(void*))));
292
293 #if GMX_THREAD_MPI
294     writer->writeLine("MPI library:        thread_mpi");
295 #elif GMX_MPI
296 #    if HAVE_CUDA_AWARE_MPI
297     writer->writeLine("MPI library:        MPI (CUDA-aware)");
298 #    else
299     writer->writeLine("MPI library:        MPI");
300 #    endif
301 #else
302     writer->writeLine("MPI library:        none");
303 #endif
304 #if GMX_OPENMP
305     writer->writeLine(formatString("OpenMP support:     enabled (GMX_OPENMP_MAX_THREADS = %d)",
306                                    GMX_OPENMP_MAX_THREADS));
307 #else
308     writer->writeLine("OpenMP support:     disabled");
309 #endif
310     writer->writeLine(formatString("GPU support:        %s", getGpuImplementationString()));
311     writer->writeLine(formatString("SIMD instructions:  %s", GMX_SIMD_STRING));
312     writer->writeLine(formatString("FFT library:        %s", getFftDescriptionString()));
313 #if GMX_TARGET_X86
314     writer->writeLine(formatString("RDTSCP usage:       %s", GMX_USE_RDTSCP ? "enabled" : "disabled"));
315 #endif
316 #if GMX_USE_TNG
317     writer->writeLine("TNG support:        enabled");
318 #else
319     writer->writeLine("TNG support:        disabled");
320 #endif
321 #if GMX_USE_HWLOC
322     writer->writeLine(formatString("Hwloc support:      hwloc-%s", HWLOC_VERSION));
323 #else
324     writer->writeLine("Hwloc support:      disabled");
325 #endif
326 #if HAVE_EXTRAE
327     unsigned major, minor, revision;
328     Extrae_get_version(&major, &minor, &revision);
329     writer->writeLine(formatString(
330             "Tracing support:    enabled. Using Extrae-%d.%d.%d", major, minor, revision));
331 #else
332     writer->writeLine("Tracing support:    disabled");
333 #endif
334
335
336     /* TODO: The below strings can be quite long, so it would be nice to wrap
337      * them. Can wait for later, as the master branch has ready code to do all
338      * that. */
339     writer->writeLine(formatString("C compiler:         %s", BUILD_C_COMPILER));
340     writer->writeLine(formatString(
341             "C compiler flags:   %s %s", BUILD_CFLAGS, CMAKE_BUILD_CONFIGURATION_C_FLAGS));
342     writer->writeLine(formatString("C++ compiler:       %s", BUILD_CXX_COMPILER));
343     writer->writeLine(formatString(
344             "C++ compiler flags: %s %s", BUILD_CXXFLAGS, CMAKE_BUILD_CONFIGURATION_CXX_FLAGS));
345 #ifdef HAVE_LIBMKL
346     /* MKL might be used for LAPACK/BLAS even if FFTs use FFTW, so keep it separate */
347     writer->writeLine(formatString(
348             "Linked with Intel MKL version %d.%d.%d.", __INTEL_MKL__, __INTEL_MKL_MINOR__, __INTEL_MKL_UPDATE__));
349 #endif
350 #if GMX_GPU_OPENCL
351     writer->writeLine(formatString("OpenCL include dir: %s", OPENCL_INCLUDE_DIR));
352     writer->writeLine(formatString("OpenCL library:     %s", OPENCL_LIBRARY));
353     writer->writeLine(formatString("OpenCL version:     %s", OPENCL_VERSION_STRING));
354 #endif
355 #if GMX_GPU_CUDA
356     writer->writeLine(formatString("CUDA compiler:      %s", CUDA_COMPILER_INFO));
357     writer->writeLine(formatString(
358             "CUDA compiler flags:%s %s", CUDA_COMPILER_FLAGS, CMAKE_BUILD_CONFIGURATION_CXX_FLAGS));
359     writer->writeLine("CUDA driver:        " + gmx::getCudaDriverVersionString());
360     writer->writeLine("CUDA runtime:       " + gmx::getCudaRuntimeVersionString());
361 #endif
362 #if GMX_SYCL_DPCPP
363     writer->writeLine(formatString("SYCL DPCPP flags:   %s", SYCL_DPCPP_COMPILER_FLAGS));
364 #endif
365 #if GMX_SYCL_HIPSYCL
366     writer->writeLine(formatString("hipSYCL launcher:   %s", SYCL_HIPSYCL_COMPILER_LAUNCHER));
367     writer->writeLine(formatString("hipSYCL flags:      %s", SYCL_HIPSYCL_COMPILER_FLAGS));
368     writer->writeLine(formatString("hipSYCL platforms:  %s", SYCL_HIPSYCL_PLATFORMS));
369 #endif
370 }
371
372 //! \endcond
373
374 } // namespace
375
376 namespace gmx
377 {
378
379 BinaryInformationSettings::BinaryInformationSettings() :
380     bExtendedInfo_(false),
381     bCopyright_(false),
382     bProcessId_(false),
383     bGeneratedByHeader_(false),
384     prefix_(""),
385     suffix_("")
386 {
387 }
388
389 void printBinaryInformation(FILE* fp, const IProgramContext& programContext)
390 {
391     TextWriter writer(fp);
392     printBinaryInformation(&writer, programContext, BinaryInformationSettings());
393 }
394
395 void printBinaryInformation(FILE*                            fp,
396                             const IProgramContext&           programContext,
397                             const BinaryInformationSettings& settings)
398 {
399     try
400     {
401         TextWriter writer(fp);
402         printBinaryInformation(&writer, programContext, settings);
403     }
404     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
405 }
406
407 void printBinaryInformation(TextWriter*                      writer,
408                             const IProgramContext&           programContext,
409                             const BinaryInformationSettings& settings)
410 {
411     // TODO Perhaps the writer could be configured with the prefix and
412     // suffix strings from the settings?
413     const char* prefix          = settings.prefix_;
414     const char* suffix          = settings.suffix_;
415     const char* precisionString = "";
416 #if GMX_DOUBLE
417     precisionString = " (double precision)";
418 #endif
419     const char* const name = programContext.displayName();
420     if (settings.bGeneratedByHeader_)
421     {
422         writer->writeLine(formatString("%sCreated by:%s", prefix, suffix));
423     }
424     // TODO: It would be nice to know here whether we are really running a
425     // Gromacs binary or some other binary that is calling Gromacs; we
426     // could then print "%s is part of GROMACS" or some alternative text.
427     std::string title = formatString(":-) GROMACS - %s, %s%s (-:", name, gmx_version(), precisionString);
428     const int indent =
429             centeringOffset(78 - std::strlen(prefix) - std::strlen(suffix), title.length()) + 1;
430     writer->writeLine(formatString("%s%*c%s%s", prefix, indent, ' ', title.c_str(), suffix));
431     writer->writeLine(formatString("%s%s", prefix, suffix));
432     if (settings.bCopyright_)
433     {
434         GMX_RELEASE_ASSERT(prefix[0] == '\0' && suffix[0] == '\0',
435                            "Prefix/suffix not supported with copyright");
436         printCopyright(writer);
437         writer->ensureEmptyLine();
438         // This line is printed again after the copyright notice to make it
439         // appear together with all the other information, so that it is not
440         // necessary to read stuff above the copyright notice.
441         // The line above the copyright notice puts the copyright notice is
442         // context, though.
443         writer->writeLine(formatString(
444                 "%sGROMACS:      %s, version %s%s%s", prefix, name, gmx_version(), precisionString, suffix));
445     }
446     const char* const binaryPath = programContext.fullBinaryPath();
447     if (!gmx::isNullOrEmpty(binaryPath))
448     {
449         writer->writeLine(formatString("%sExecutable:   %s%s", prefix, binaryPath, suffix));
450     }
451     const gmx::InstallationPrefixInfo installPrefix = programContext.installationPrefix();
452     if (!gmx::isNullOrEmpty(installPrefix.path))
453     {
454         writer->writeLine(formatString("%sData prefix:  %s%s%s",
455                                        prefix,
456                                        installPrefix.path,
457                                        installPrefix.bSourceLayout ? " (source tree)" : "",
458                                        suffix));
459     }
460     const std::string workingDir = Path::getWorkingDirectory();
461     if (!workingDir.empty())
462     {
463         writer->writeLine(formatString("%sWorking dir:  %s%s", prefix, workingDir.c_str(), suffix));
464     }
465     if (settings.bProcessId_)
466     {
467         writer->writeLine(formatString("%sProcess ID:   %d%s", prefix, gmx_getpid(), suffix));
468     }
469     const char* const commandLine = programContext.commandLine();
470     if (!gmx::isNullOrEmpty(commandLine))
471     {
472         writer->writeLine(formatString(
473                 "%sCommand line:%s\n%s  %s%s", prefix, suffix, prefix, commandLine, suffix));
474     }
475     if (settings.bExtendedInfo_)
476     {
477         GMX_RELEASE_ASSERT(prefix[0] == '\0' && suffix[0] == '\0',
478                            "Prefix/suffix not supported with extended info");
479         writer->ensureEmptyLine();
480         gmx_print_version_info(writer);
481     }
482 }
483
484 } // namespace gmx