cpp/mmapbuf/Buffer.cpp (118 lines of code) (raw):
/**
* Copyright 2004-present, Facebook, Inc.
*
* 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 "Buffer.h"
#include <system_error>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstring>
#include <memory>
#include <errno.h>
#include <fb/log.h>
namespace facebook {
namespace profilo {
namespace mmapbuf {
namespace {
static size_t calculateBufferSize(size_t entryCount) {
return sizeof(MmapBufferPrefix) +
TraceBuffer::calculateAllocationSize(entryCount);
}
} // namespace
Buffer::Buffer(std::string const& path, size_t entryCount) {
int fd = open(path.c_str(), O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd == -1) {
throw std::system_error(
errno, std::system_category(), "Cannot open file " + path);
}
size_t totalSize = calculateBufferSize(entryCount);
// In order to allocate file size of N bytes we seek to (N-1)th position and
// just write single byte at the end. This allows us to avoid filling the
// whole file before mmap() call.
auto res = lseek(fd, totalSize - 1, SEEK_SET);
if (res == -1) {
close(fd);
throw std::system_error(
errno, std::system_category(), "Cannot lseek file " + path);
}
char byte = 0x00;
res = write(fd, &byte, 1);
if (res == -1) {
close(fd);
throw std::system_error(
errno, std::system_category(), "Cannot write a byte " + path);
}
void* map_ptr =
mmap(nullptr, totalSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (map_ptr == MAP_FAILED) {
close(fd);
throw std::system_error(
errno, std::system_category(), "Cannot mmap file " + path);
}
close(fd);
// Initialize MmapBuffer in the created mmap area.
auto map_chr = reinterpret_cast<char*>(map_ptr);
// Initialize a prefix at the beginning of the space,
// and set buffer to immediately after the prefix.
prefix = new (map_chr) MmapBufferPrefix();
buffer = map_chr + sizeof(MmapBufferPrefix);
this->path = path;
this->entryCount = entryCount;
this->totalByteSize = totalSize;
this->file_backed_ = true;
lfrb_ = TraceBuffer::allocateAt(entryCount, (void*)buffer);
}
Buffer::Buffer(size_t entryCount) {
size_t totalSize = calculateBufferSize(entryCount);
auto mem = new char[totalSize];
prefix = new (mem) MmapBufferPrefix();
buffer = mem + sizeof(MmapBufferPrefix);
this->totalByteSize = totalSize;
this->entryCount = entryCount;
this->file_backed_ = false;
lfrb_ = TraceBuffer::allocateAt(entryCount, (void*)buffer);
}
Buffer::Buffer(Buffer&& other)
: path(std::move(other.path)),
entryCount(other.entryCount),
totalByteSize(other.totalByteSize),
prefix(other.prefix),
buffer(other.buffer),
file_backed_(other.file_backed_),
lfrb_(std::move(other.lfrb_)) {
other.entryCount = 0;
other.totalByteSize = 0;
other.prefix = nullptr;
other.buffer = nullptr;
other.file_backed_ = false;
}
Buffer& Buffer::operator=(Buffer&& other) {
this->~Buffer();
path = std::move(other.path);
entryCount = other.entryCount;
totalByteSize = other.totalByteSize;
prefix = other.prefix;
buffer = other.buffer;
lfrb_ = std::move(other.lfrb_);
other.buffer = other.prefix = nullptr;
other.entryCount = other.totalByteSize = 0;
other.file_backed_ = false;
return *this;
}
Buffer::~Buffer() {
if (buffer == nullptr) {
return;
}
prefix->~MmapBufferPrefix();
if (file_backed_) {
// mmap mode: remove the mapping and the file
munmap(prefix, totalByteSize);
unlink(path.c_str());
} else {
// anonymous mode: delete the backing memory
delete[] reinterpret_cast<char*>(prefix);
}
}
void Buffer::rename(std::string const& new_path) {
if (::rename(path.c_str(), new_path.c_str())) {
throw std::system_error(
errno,
std::system_category(),
"Failed to rename mmap buffer file " + path + " to " + new_path);
}
path = new_path;
}
} // namespace mmapbuf
} // namespace profilo
} // namespace facebook