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