cachelib/shm/ShmCommon.cpp (111 lines of code) (raw):
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cachelib/shm/ShmCommon.h"
#include <folly/FileUtil.h>
#include <folly/Random.h>
#include <folly/Range.h>
#include <folly/String.h>
#include <folly/logging/xlog.h>
#include <sys/types.h>
namespace facebook {
namespace cachelib {
namespace detail {
size_t getPageSize(PageSizeT pageSize) {
static size_t sizes[] = {static_cast<size_t>(sysconf(_SC_PAGESIZE)),
2 * 1024 * 1024, 1024 * 1024 * 1024};
const long pagesize = sizes[pageSize];
XDCHECK_NE(pagesize, -1);
XDCHECK_GT(pagesize, 0);
return pagesize;
}
bool isPageAlignedSize(size_t size, PageSizeT p) {
return ((size != 0) && (size % getPageSize(p) == 0));
}
size_t getPageAlignedSize(size_t size, PageSizeT p) {
const auto pageSize = getPageSize(p);
if (size == 0) {
return pageSize;
}
auto delta = size % pageSize;
return delta == 0 ? size : size + pageSize - delta;
}
bool isPageAlignedAddr(void* addr, PageSizeT p) {
return ((uintptr_t)addr) % getPageSize(p) == 0;
}
size_t pageAligned(size_t size, PageSizeT p) {
const auto pageSize = getPageSize(p);
XDCHECK(!(pageSize & (pageSize - 1)));
return 1 + ((size - 1) | (pageSize - 1));
}
namespace {
std::vector<folly::StringPiece> getSmapLines(const std::string& smapContent) {
std::vector<folly::StringPiece> lines;
folly::split("\n", smapContent, lines, true);
XDCHECK(!lines.empty());
return lines;
}
size_t getAddressVal(folly::StringPiece addr) {
// addresses are in base 16
const size_t ret = strtoull(addr.data(), nullptr, 16);
XDCHECK_NE(ret, 0u);
return ret;
}
bool lineAddressMatches(folly::StringPiece line, uintptr_t addr) {
// line should be of form
// 006de000-01397000 rw-p 00000000 00:00 0 [heap]
std::vector<folly::StringPiece> tokens;
// split into tokens by space
folly::split(" ", line, tokens, /* ignore empty */ true);
XDCHECK(!tokens.empty());
folly::StringPiece startAddr;
folly::StringPiece endAddr;
// split the first token using the '-' separator
if (!folly::split("-", tokens[0], startAddr, endAddr)) {
throw std::invalid_argument(
folly::sformat("Invalid address field {}", tokens[0]));
}
// parse the address values.
size_t start = getAddressVal(startAddr);
size_t end = getAddressVal(endAddr);
return start <= addr && end >= addr;
}
bool isAddressLine(folly::StringPiece line) {
// address lines contain lots of fields before the first :
// 006de000-01397000 rw-p 00000000 00:00 0 [heap]
folly::StringPiece first, second;
folly::split(":", line, first, second);
return first.find(' ') != std::string::npos;
}
} // namespace
PageSizeT getPageSizeInSMap(void* addr) {
std::string smapContent;
folly::readFile("/proc/self/smaps", smapContent);
const auto smapLines = getSmapLines(smapContent);
bool foundMatching = false;
for (auto line : smapLines) {
const bool isAddr = isAddressLine(line);
if (!foundMatching && isAddr &&
lineAddressMatches(line, reinterpret_cast<uintptr_t>(addr))) {
foundMatching = true;
continue;
}
if (!foundMatching) {
continue;
}
XDCHECK(foundMatching);
XDCHECK(!isAddr);
// Format is the following
// KernelPageSize: 4 kB
folly::StringPiece fieldName, value;
folly::split(":", line, fieldName, value);
if (fieldName != "MMUPageSize") {
continue;
}
value = folly::skipWhitespace(value);
folly::StringPiece sizeVal;
folly::StringPiece unitVal;
folly::split(" ", value, sizeVal, unitVal);
XDCHECK_EQ(unitVal, "kB");
size_t size = folly::to<size_t>(sizeVal) * 1024;
if (size == getPageSize(PageSizeT::TWO_MB)) {
return PageSizeT::TWO_MB;
} else if (size == getPageSize(PageSizeT::ONE_GB)) {
return PageSizeT::ONE_GB;
} else {
XDCHECK_EQ(size, getPageSize());
return PageSizeT::NORMAL;
}
}
throw std::invalid_argument("address mapping not found in /proc/self/smaps");
}
} // namespace detail
} // namespace cachelib
} // namespace facebook