1 // processinfo_linux2.cpp
2 
3 
4 /**
5  *    Copyright (C) 2018-present MongoDB, Inc.
6  *
7  *    This program is free software: you can redistribute it and/or modify
8  *    it under the terms of the Server Side Public License, version 1,
9  *    as published by MongoDB, Inc.
10  *
11  *    This program 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  *    Server Side Public License for more details.
15  *
16  *    You should have received a copy of the Server Side Public License
17  *    along with this program. If not, see
18  *    <http://www.mongodb.com/licensing/server-side-public-license>.
19  *
20  *    As a special exception, the copyright holders give permission to link the
21  *    code of portions of this program with the OpenSSL library under certain
22  *    conditions as described in each individual source file and distribute
23  *    linked combinations including the program with the OpenSSL library. You
24  *    must comply with the Server Side Public License in all respects for
25  *    all of the code used other than as permitted herein. If you modify file(s)
26  *    with this exception, you may extend this exception to your version of the
27  *    file(s), but you are not obligated to do so. If you do not wish to do so,
28  *    delete this exception statement from your version. If you delete this
29  *    exception statement from all source files in the program, then also delete
30  *    it in the license file.
31  */
32 
33 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kControl
34 
35 #include "mongo/platform/basic.h"
36 
37 #include "processinfo.h"
38 
39 #include <iostream>
40 #include <malloc.h>
41 #include <pcrecpp.h>
42 #include <sched.h>
43 #include <stdio.h>
44 #include <sys/mman.h>
45 #include <sys/resource.h>
46 #include <sys/time.h>
47 #include <sys/utsname.h>
48 #include <unistd.h>
49 #ifdef __UCLIBC__
50 #include <features.h>
51 #else
52 #include <gnu/libc-version.h>
53 #endif
54 
55 #include <boost/filesystem.hpp>
56 #include <boost/none.hpp>
57 #include <boost/optional.hpp>
58 #include <pcrecpp.h>
59 
60 #include "mongo/util/file.h"
61 #include "mongo/util/log.h"
62 
63 using namespace std;
64 
65 #define KLONG long
66 #define KLF "l"
67 
68 namespace mongo {
69 
70 class LinuxProc {
71 public:
LinuxProc(ProcessId pid)72     LinuxProc(ProcessId pid) {
73         char name[128];
74         sprintf(name, "/proc/%d/stat", pid.asUInt32());
75         FILE* f = fopen(name, "r");
76         if (!f) {
77             stringstream ss;
78             ss << "couldn't open [" << name << "] " << errnoWithDescription();
79             string s = ss.str();
80             msgasserted(13538, s.c_str());
81         }
82         int found = fscanf(f,
83                            "%d %127s %c "
84                            "%d %d %d %d %d "
85                            "%lu %lu %lu %lu %lu "
86                            "%lu %lu %ld %ld " /* utime stime cutime cstime */
87                            "%ld %ld "
88                            "%ld "
89                            "%ld "
90                            "%lu " /* start_time */
91                            "%lu "
92                            "%ld "  // rss
93                            "%lu %" KLF "u %" KLF "u %" KLF "u %" KLF "u %" KLF "u "
94                            /*
95                              "%*s %*s %*s %*s "
96                              "%"KLF"u %*lu %*lu "
97                              "%d %d "
98                              "%lu %lu"
99                            */
100 
101                            ,
102 
103                            &_pid,
104                            _comm,
105                            &_state,
106                            &_ppid,
107                            &_pgrp,
108                            &_session,
109                            &_tty,
110                            &_tpgid,
111                            &_flags,
112                            &_min_flt,
113                            &_cmin_flt,
114                            &_maj_flt,
115                            &_cmaj_flt,
116                            &_utime,
117                            &_stime,
118                            &_cutime,
119                            &_cstime,
120                            &_priority,
121                            &_nice,
122                            &_nlwp,
123                            &_alarm,
124                            &_start_time,
125                            &_vsize,
126                            &_rss,
127                            &_rss_rlim,
128                            &_start_code,
129                            &_end_code,
130                            &_start_stack,
131                            &_kstk_esp,
132                            &_kstk_eip
133 
134                            /*
135                              &_wchan,
136                              &_exit_signal, &_processor,
137                              &_rtprio, &_sched
138                            */
139                            );
140         if (found == 0) {
141             log() << "system error: reading proc info";
142         }
143         fclose(f);
144     }
145 
getVirtualMemorySize()146     unsigned long getVirtualMemorySize() {
147         return _vsize;
148     }
149 
getResidentSizeInPages()150     unsigned long getResidentSizeInPages() {
151         return (unsigned long)_rss;
152     }
153 
154     int _pid;
155     // The process ID.
156 
157     char _comm[128];
158     // The filename of the executable, in parentheses.  This is visible whether or not the
159     // executable is swapped out.
160 
161     char _state;
162     // One character from the string "RSDZTW" where R is running, S is sleeping in an interruptible
163     // wait, D is waiting in uninterruptible disk sleep, Z is zombie, T is traced or stopped (on a
164     // signal), and W is paging.
165 
166     int _ppid;
167     // The PID of the parent.
168 
169     int _pgrp;
170     // The process group ID of the process.
171 
172     int _session;
173     // The session ID of the process.
174 
175     int _tty;
176     // The tty the process uses.
177 
178     int _tpgid;
179     // The process group ID of the process which currently owns the tty that the process is
180     // connected to.
181 
182     unsigned long _flags;  // %lu
183     // The  kernel flags word of the process. For bit meanings, see the PF_* defines in
184     // <linux/sched.h>.  Details depend on the kernel version.
185 
186     unsigned long _min_flt;  // %lu
187     // The number of minor faults the process has made which have not required loading a memory page
188     // from disk.
189 
190     unsigned long _cmin_flt;  // %lu
191     // The number of minor faults that the process
192 
193     unsigned long _maj_flt;  // %lu
194     // The number of major faults the process has made which have required loading a memory page
195     // from disk.
196 
197     unsigned long _cmaj_flt;  // %lu
198     // The number of major faults that the process
199 
200     unsigned long _utime;  // %lu
201     // The number of jiffies that this process has been scheduled in user mode.
202 
203     unsigned long _stime;  //  %lu
204     // The number of jiffies that this process has been scheduled in kernel mode.
205 
206     long _cutime;  // %ld
207     // The number of jiffies that this removed field.
208 
209     long _cstime;  // %ld
210 
211     long _priority;
212     long _nice;
213 
214     long _nlwp;  // %ld
215     // number of threads
216 
217     unsigned long _alarm;
218     // The time in jiffies before the next SIGALRM is sent to the process due to an interval timer.
219     // (unused since 2.6.17)
220 
221     unsigned long _start_time;  // %lu
222     // The time in jiffies the process started after system boot.
223 
224     unsigned long _vsize;  // %lu
225     // Virtual memory size in bytes.
226 
227     long _rss;  // %ld
228     // Resident Set Size: number of pages the process has in real memory, minus 3 for administrative
229     // purposes. This is just the pages which count  towards  text,  data, or stack space.  This
230     // does not include pages which have not been demand-loaded in, or which are swapped out
231 
232     unsigned long _rss_rlim;  // %lu
233     // Current limit in bytes on the rss of the process (usually 4294967295 on i386).
234 
235     unsigned long _start_code;  // %lu
236     // The address above which program text can run.
237 
238     unsigned long _end_code;  // %lu
239     // The address below which program text can run.
240 
241     unsigned long _start_stack;  // %lu
242     // The address of the start of the stack.
243 
244     unsigned long _kstk_esp;  // %lu
245     // The current value of esp (stack pointer), as found in the kernel stack page for the process.
246 
247     unsigned long _kstk_eip;  // %lu
248     // The current EIP (instruction pointer).
249 };
250 
251 namespace {
252 
253 // As described in the /proc/[pid]/mountinfo section of `man 5 proc`:
254 //
255 // 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
256 // |  |  |    |     |     |          |          |      |     |
257 // (1)(2)(3:4)(5)   (6)   (7)        (8)        (9)   (10)   (11)
258 struct MountRecord {
parseLinemongo::__anon35434b100111::MountRecord259     bool parseLine(const std::string& line) {
260         static const pcrecpp::RE kRe{
261             //   (1)   (2)   (3)   (4)   (5)   (6)   (7)   (8)                (9)   (10)  (11)
262             R"re((\d+) (\d+) (\d+):(\d+) (\S+) (\S+) (\S+) ((?:\S+:\S+ ?)*) - (\S+) (\S+) (\S+))re"};
263         return kRe.FullMatch(line,
264                              &mountId,
265                              &parentId,
266                              &major,
267                              &minor,
268                              &root,
269                              &mountPoint,
270                              &options,
271                              &fields,
272                              &type,
273                              &source,
274                              &superOpt);
275     }
276 
appendBSONmongo::__anon35434b100111::MountRecord277     void appendBSON(BSONObjBuilder& bob) const {
278         bob.append("mountId", mountId)
279             .append("parentId", parentId)
280             .append("major", major)
281             .append("minor", minor)
282             .append("root", root)
283             .append("mountPoint", mountPoint)
284             .append("options", options)
285             .append("fields", fields)
286             .append("type", type)
287             .append("source", source)
288             .append("superOpt", superOpt);
289     }
290 
291     int mountId;             //  (1) unique ID for the mount
292     int parentId;            //  (2) the ID of the parent mount (self for the root mount)
293     int major;               //  (3) major block device number (see stat(2))
294     int minor;               //  (4) minor block device number
295     std::string root;        //  (5) path in filesystem forming the root
296     std::string mountPoint;  //  (6) the mount point relative to the process's root
297     std::string options;     //  (7) per-mount options (see mount(2)).
298     std::string fields;      //  (8) zero or more: "tag[:value]" fields
299     std::string type;        //  (9) filesystem type: "type[.subtype]"
300     std::string source;      //  (10) fs-specific information or "none"
301     std::string superOpt;    //  (11) per-superblock options (see mount(2))
302 };
303 
appendMountInfo(BSONObjBuilder & bob)304 void appendMountInfo(BSONObjBuilder& bob) {
305     std::ifstream ifs("/proc/self/mountinfo");
306     if (!ifs)
307         return;
308     BSONArrayBuilder arr = bob.subarrayStart("mountInfo");
309     std::string line;
310     MountRecord rec;
311     while (ifs && getline(ifs, line)) {
312         if (rec.parseLine(line)) {
313             auto bob = BSONObjBuilder(arr.subobjStart());
314             rec.appendBSON(bob);
315         }
316     }
317 }
318 
319 class CpuInfoParser {
320 public:
321     struct LineProcessor {
322         pcrecpp::RE regex;
323         std::function<void(const std::string&)> f;
324     };
325     std::vector<LineProcessor> lineProcessors;
326     std::function<void()> recordProcessor;
run()327     void run() {
328         std::ifstream f("/proc/cpuinfo");
329         if (!f)
330             return;
331 
332         bool readSuccess;
333         bool unprocessed = false;
334         static const pcrecpp::RE lineRegex(R"re((.*?)\s*:\s*(.*))re");
335         do {
336             std::string fstr;
337             readSuccess = f && std::getline(f, fstr);
338             if (readSuccess && !fstr.empty()) {
339                 std::string key;
340                 std::string value;
341                 if (!lineRegex.FullMatch(fstr, &key, &value))
342                     continue;
343                 for (auto&& lp : lineProcessors) {
344                     if (lp.regex.FullMatch(key))
345                         lp.f(value);
346                 }
347                 unprocessed = true;
348             } else if (unprocessed) {
349                 recordProcessor();
350                 unprocessed = false;
351             }
352         } while (readSuccess);
353     }
354 };
355 
356 }  // namespace
357 
358 class LinuxSysHelper {
359 public:
360     /**
361     * Read the first 1023 bytes from a file
362     */
readLineFromFile(const char * fname)363     static string readLineFromFile(const char* fname) {
364         FILE* f;
365         char fstr[1024] = {0};
366 
367         f = fopen(fname, "r");
368         if (f != NULL) {
369             if (fgets(fstr, 1023, f) != NULL)
370                 fstr[strlen(fstr) < 1 ? 0 : strlen(fstr) - 1] = '\0';
371             fclose(f);
372         }
373         return fstr;
374     }
375 
376 
377     /**
378      * count the number of physical cores
379      */
getNumPhysicalCores(int & physicalCores)380     static void getNumPhysicalCores(int& physicalCores) {
381 
382         /* In /proc/cpuinfo core ids are only unique within a particular physical unit, AKA a cpu
383          * package, so to count the total cores we need to count the unique pairs of core id and
384          * physical id*/
385         struct CpuId {
386             std::string core;
387             std::string physical;
388         };
389 
390         CpuId parsedCpuId;
391 
392         auto cmp = [](auto&& a, auto&& b) {
393             auto tupLens = [](auto&& o) { return std::tie(o.core, o.physical); };
394             return tupLens(a) < tupLens(b);
395         };
396         std::set<CpuId, decltype(cmp)> cpuIds(cmp);
397 
398         CpuInfoParser cpuInfoParser{
399             {
400                 {"physical id", [&](const std::string& value) { parsedCpuId.physical = value; }},
401                 {"core id", [&](const std::string& value) { parsedCpuId.core = value; }},
402             },
403             [&]() {
404                 cpuIds.insert(parsedCpuId);
405                 parsedCpuId = CpuId{};
406             }};
407         cpuInfoParser.run();
408 
409         physicalCores = cpuIds.size();
410     }
411 
412     /**
413     * Get some details about the CPU
414     */
getCpuInfo(int & procCount,std::string & freq,std::string & features)415     static void getCpuInfo(int& procCount, std::string& freq, std::string& features) {
416 
417         procCount = 0;
418 
419         CpuInfoParser cpuInfoParser{
420             {
421 #ifdef __s390x__
422                 {R"re(processor\s+\d+)re", [&](const std::string& value) { procCount++; }},
423                 {"cpu MHz static", [&](const std::string& value) { freq = value; }},
424                 {"features", [&](const std::string& value) { features = value; }},
425 #else
426                 {"processor", [&](const std::string& value) { procCount++; }},
427                 {"cpu MHz", [&](const std::string& value) { freq = value; }},
428                 {"flags", [&](const std::string& value) { features = value; }},
429 #endif
430             },
431             []() {}};
432         cpuInfoParser.run();
433     }
434 
435     /**
436     * Determine linux distro and version
437     */
getLinuxDistro(string & name,string & version)438     static void getLinuxDistro(string& name, string& version) {
439         char buf[4096] = {0};
440 
441         // try lsb file first
442         if (boost::filesystem::exists("/etc/lsb-release")) {
443             File f;
444             f.open("/etc/lsb-release", true);
445             if (!f.is_open() || f.bad())
446                 return;
447             f.read(0, buf, f.len() > 4095 ? 4095 : f.len());
448 
449             // find the distribution name and version in the contents.
450             // format:  KEY=VAL\n
451             string contents = buf;
452             unsigned lineCnt = 0;
453             try {
454                 while (lineCnt < contents.length() - 1 &&
455                        contents.substr(lineCnt).find('\n') != string::npos) {
456                     // until we hit the last newline or eof
457                     string line = contents.substr(lineCnt, contents.substr(lineCnt).find('\n'));
458                     lineCnt += contents.substr(lineCnt).find('\n') + 1;
459                     size_t delim = line.find('=');
460                     string key = line.substr(0, delim);
461                     string val = line.substr(delim + 1);  // 0-based offset of delim
462                     if (key.compare("DISTRIB_ID") == 0)
463                         name = val;
464                     if (string(key).compare("DISTRIB_RELEASE") == 0)
465                         version = val;
466                 }
467             } catch (const std::out_of_range& e) {
468                 // attempted to get invalid substr
469             }
470             // return with lsb-release data if we found both the name and version
471             if (!name.empty() && !version.empty()) {
472                 return;
473             }
474         }
475 
476         // try known flat-text file locations
477         // format: Slackware-x86_64 13.0, Red Hat Enterprise Linux Server release 5.6 (Tikanga),
478         // etc.
479         typedef vector<string> pathvec;
480         pathvec paths;
481         pathvec::const_iterator i;
482         bool found = false;
483         paths.push_back("/etc/system-release");
484         paths.push_back("/etc/redhat-release");
485         paths.push_back("/etc/gentoo-release");
486         paths.push_back("/etc/novell-release");
487         paths.push_back("/etc/gentoo-release");
488         paths.push_back("/etc/SuSE-release");
489         paths.push_back("/etc/SUSE-release");
490         paths.push_back("/etc/sles-release");
491         paths.push_back("/etc/debian_release");
492         paths.push_back("/etc/slackware-version");
493         paths.push_back("/etc/centos-release");
494         paths.push_back("/etc/os-release");
495 
496         for (i = paths.begin(); i != paths.end(); ++i) {
497             // for each path
498             if (boost::filesystem::exists(*i)) {
499                 // if the file exists, break
500                 found = true;
501                 break;
502             }
503         }
504 
505         if (found) {
506             // found a file
507             File f;
508             f.open(i->c_str(), true);
509             if (!f.is_open() || f.bad())
510                 // file exists but can't be opened
511                 return;
512 
513             // read up to 512 bytes
514             int len = f.len() > 512 ? 512 : f.len();
515             f.read(0, buf, len);
516             buf[len] = '\0';
517             name = buf;
518             size_t nl = 0;
519             if ((nl = name.find('\n', nl)) != string::npos)
520                 // stop at first newline
521                 name.erase(nl);
522         } else {
523             name = "unknown";
524         }
525 
526         // There is no standard format for name and version so use the kernel version.
527         version = "Kernel ";
528         version += LinuxSysHelper::readLineFromFile("/proc/sys/kernel/osrelease");
529     }
530 
531     /**
532     * Get system memory total
533     */
getSystemMemorySize()534     static unsigned long long getSystemMemorySize() {
535         string meminfo = readLineFromFile("/proc/meminfo");
536         size_t lineOff = 0;
537         if (!meminfo.empty() && (lineOff = meminfo.find("MemTotal")) != string::npos) {
538             // found MemTotal line.  capture everything between 'MemTotal:' and ' kB'.
539             lineOff = meminfo.substr(lineOff).find(':') + 1;
540             meminfo = meminfo.substr(lineOff, meminfo.substr(lineOff).find("kB") - 1);
541             lineOff = 0;
542 
543             // trim whitespace and append 000 to replace kB.
544             while (isspace(meminfo.at(lineOff)))
545                 lineOff++;
546             meminfo = meminfo.substr(lineOff);
547 
548             unsigned long long systemMem = 0;
549             if (mongo::parseNumberFromString(meminfo, &systemMem).isOK()) {
550                 return systemMem * 1024;  // convert from kB to bytes
551             } else
552                 log() << "Unable to collect system memory information";
553         }
554         return 0;
555     }
556 
557     /**
558     * Get memory limit for the process.
559     * If memory is being limited by the applied control group and it's less
560     * than the OS system memory (default cgroup limit is ulonglong max) let's
561     * return the actual memory we'll have available to the process.
562     */
getMemorySizeLimit()563     static unsigned long long getMemorySizeLimit() {
564         unsigned long long systemMemBytes = getSystemMemorySize();
565         unsigned long long cgroupMemBytes = 0;
566         std::string cgmemlimit = readLineFromFile("/sys/fs/cgroup/memory/memory.limit_in_bytes");
567         if (!cgmemlimit.empty() &&
568             mongo::parseNumberFromString(cgmemlimit, &cgroupMemBytes).isOK()) {
569             return std::min(systemMemBytes, cgroupMemBytes);
570         }
571         return systemMemBytes;
572     }
573 };
574 
575 
ProcessInfo(ProcessId pid)576 ProcessInfo::ProcessInfo(ProcessId pid) : _pid(pid) {}
577 
~ProcessInfo()578 ProcessInfo::~ProcessInfo() {}
579 
supported()580 bool ProcessInfo::supported() {
581     return true;
582 }
583 
584 // get the number of CPUs available to the current process
getNumCoresForProcess()585 boost::optional<unsigned long> ProcessInfo::getNumCoresForProcess() {
586     cpu_set_t set;
587 
588     if (sched_getaffinity(0, sizeof(cpu_set_t), &set) == 0) {
589 #ifdef CPU_COUNT  // glibc >= 2.6 has CPU_COUNT defined
590         return CPU_COUNT(&set);
591 #else
592         unsigned long count = 0;
593         for (size_t i = 0; i < CPU_SETSIZE; i++)
594             if (CPU_ISSET(i, &set))
595                 count++;
596         if (count > 0)
597             return count;
598 #endif
599     }
600 
601     return boost::none;
602 }
603 
getVirtualMemorySize()604 int ProcessInfo::getVirtualMemorySize() {
605     LinuxProc p(_pid);
606     return (int)(p.getVirtualMemorySize() / (1024.0 * 1024));
607 }
608 
getResidentSize()609 int ProcessInfo::getResidentSize() {
610     LinuxProc p(_pid);
611     return (int)((p.getResidentSizeInPages() * getPageSize()) / (1024.0 * 1024));
612 }
613 
getSystemMemoryPressurePercentage()614 double ProcessInfo::getSystemMemoryPressurePercentage() {
615     return 0.0;
616 }
617 
getExtraInfo(BSONObjBuilder & info)618 void ProcessInfo::getExtraInfo(BSONObjBuilder& info) {
619     struct rusage ru;
620     getrusage(RUSAGE_SELF, &ru);
621     if (ru.ru_majflt <= std::numeric_limits<long long>::max())
622         info.appendNumber("page_faults", static_cast<long long>(ru.ru_majflt));
623     else
624         info.appendNumber("page_faults", static_cast<double>(ru.ru_majflt));
625 }
626 
627 /**
628 * Save a BSON obj representing the host system's details
629 */
collectSystemInfo()630 void ProcessInfo::SystemInfo::collectSystemInfo() {
631     utsname unameData;
632     string distroName, distroVersion;
633     string cpuFreq, cpuFeatures;
634     int cpuCount;
635     int physicalCores;
636 
637     string verSig = LinuxSysHelper::readLineFromFile("/proc/version_signature");
638     LinuxSysHelper::getCpuInfo(cpuCount, cpuFreq, cpuFeatures);
639     LinuxSysHelper::getNumPhysicalCores(physicalCores);
640     LinuxSysHelper::getLinuxDistro(distroName, distroVersion);
641 
642     if (uname(&unameData) == -1) {
643         log() << "Unable to collect detailed system information: " << strerror(errno);
644     }
645 
646     osType = "Linux";
647     osName = distroName;
648     osVersion = distroVersion;
649     memSize = LinuxSysHelper::getSystemMemorySize();
650     memLimit = LinuxSysHelper::getMemorySizeLimit();
651     addrSize = sizeof(void*) * CHAR_BIT;
652     numCores = cpuCount;
653     pageSize = static_cast<unsigned long long>(sysconf(_SC_PAGESIZE));
654     cpuArch = unameData.machine;
655     hasNuma = checkNumaEnabled();
656 
657     BSONObjBuilder bExtra;
658     bExtra.append("versionString", LinuxSysHelper::readLineFromFile("/proc/version"));
659 #ifdef __UCLIBC__
660     stringstream ss;
661     ss << "uClibc-" << __UCLIBC_MAJOR__ << "." << __UCLIBC_MINOR__ << "." << __UCLIBC_SUBLEVEL__;
662     bExtra.append("libcVersion", ss.str());
663 #else
664     bExtra.append("libcVersion", gnu_get_libc_version());
665 #endif
666     if (!verSig.empty())
667         // optional
668         bExtra.append("versionSignature", verSig);
669 
670     bExtra.append("kernelVersion", unameData.release);
671     bExtra.append("cpuFrequencyMHz", cpuFreq);
672     bExtra.append("cpuFeatures", cpuFeatures);
673     bExtra.append("pageSize", static_cast<long long>(pageSize));
674     bExtra.append("numPages", static_cast<int>(sysconf(_SC_PHYS_PAGES)));
675     bExtra.append("maxOpenFiles", static_cast<int>(sysconf(_SC_OPEN_MAX)));
676     bExtra.append("physicalCores", physicalCores);
677 
678     _extraStats = bExtra.obj();
679 }
680 
681 /**
682 * Determine if the process is running with (cc)NUMA
683 */
checkNumaEnabled()684 bool ProcessInfo::checkNumaEnabled() {
685     bool hasMultipleNodes = false;
686     bool hasNumaMaps = false;
687 
688     try {
689         hasMultipleNodes = boost::filesystem::exists("/sys/devices/system/node/node1");
690         hasNumaMaps = boost::filesystem::exists("/proc/self/numa_maps");
691     } catch (boost::filesystem::filesystem_error& e) {
692         log() << "WARNING: Cannot detect if NUMA interleaving is enabled. "
693               << "Failed to probe \"" << e.path1().string() << "\": " << e.code().message();
694         return false;
695     }
696 
697     if (hasMultipleNodes && hasNumaMaps) {
698         // proc is populated with numa entries
699 
700         // read the second column of first line to determine numa state
701         // ('default' = enabled, 'interleave' = disabled).  Logic from version.cpp's warnings.
702         string line = LinuxSysHelper::readLineFromFile("/proc/self/numa_maps").append(" \0");
703         size_t pos = line.find(' ');
704         if (pos != string::npos && line.substr(pos + 1, 10).find("interleave") == string::npos)
705             // interleave not found;
706             return true;
707     }
708     return false;
709 }
710 
blockCheckSupported()711 bool ProcessInfo::blockCheckSupported() {
712     return true;
713 }
714 
blockInMemory(const void * start)715 bool ProcessInfo::blockInMemory(const void* start) {
716     unsigned char x = 0;
717     if (mincore(const_cast<void*>(alignToStartOfPage(start)), getPageSize(), &x)) {
718         log() << "mincore failed: " << errnoWithDescription();
719         return 1;
720     }
721     return x & 0x1;
722 }
723 
pagesInMemory(const void * start,size_t numPages,vector<char> * out)724 bool ProcessInfo::pagesInMemory(const void* start, size_t numPages, vector<char>* out) {
725     out->resize(numPages);
726     if (mincore(const_cast<void*>(alignToStartOfPage(start)),
727                 numPages * getPageSize(),
728                 reinterpret_cast<unsigned char*>(&out->front()))) {
729         log() << "mincore failed: " << errnoWithDescription();
730         return false;
731     }
732     for (size_t i = 0; i < numPages; ++i) {
733         (*out)[i] &= 0x1;
734     }
735     return true;
736 }
737 }
738