1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
//===-- SubprocessMemory.cpp ------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "SubprocessMemory.h"
#include "Error.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FormatVariadic.h"
#include <cerrno>
#ifdef __linux__
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
#endif
namespace llvm {
namespace exegesis {
#if defined(__linux__)
// The SYS_* macros for system calls are provided by the libc whereas the
// __NR_* macros are from the linux headers. This means that sometimes
// SYS_* macros might not be available for certain system calls depending
// upon the libc. This happens with the gettid syscall and bionic for
// example, so we use __NR_gettid when no SYS_gettid is available.
#ifndef SYS_gettid
#define SYS_gettid __NR_gettid
#endif
long SubprocessMemory::getCurrentTID() {
// We're using the raw syscall here rather than the gettid() function provided
// by most libcs for compatibility as gettid() was only added to glibc in
// version 2.30.
return syscall(SYS_gettid);
}
#if !defined(__ANDROID__)
Error SubprocessMemory::initializeSubprocessMemory(pid_t ProcessID) {
// Add the PID to the shared memory name so that if we're running multiple
// processes at the same time, they won't interfere with each other.
// This comes up particularly often when running the exegesis tests with
// llvm-lit. Additionally add the TID so that downstream consumers
// using multiple threads don't run into conflicts.
std::string AuxiliaryMemoryName =
formatv("/{0}auxmem{1}", getCurrentTID(), ProcessID);
int AuxiliaryMemoryFD = shm_open(AuxiliaryMemoryName.c_str(),
O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (AuxiliaryMemoryFD == -1)
return make_error<Failure>(
"Failed to create shared memory object for auxiliary memory: " +
Twine(strerror(errno)));
auto AuxiliaryMemoryFDClose =
make_scope_exit([AuxiliaryMemoryFD]() { close(AuxiliaryMemoryFD); });
if (ftruncate(AuxiliaryMemoryFD, AuxiliaryMemorySize) != 0) {
return make_error<Failure>("Truncating the auxiliary memory failed: " +
Twine(strerror(errno)));
}
SharedMemoryNames.push_back(AuxiliaryMemoryName);
return Error::success();
}
Error SubprocessMemory::addMemoryDefinition(
std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
pid_t ProcessPID) {
SharedMemoryNames.reserve(MemoryDefinitions.size());
for (auto &[Name, MemVal] : MemoryDefinitions) {
std::string SharedMemoryName =
formatv("/{0}t{1}memdef{2}", ProcessPID, getCurrentTID(), MemVal.Index);
SharedMemoryNames.push_back(SharedMemoryName);
int SharedMemoryFD =
shm_open(SharedMemoryName.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (SharedMemoryFD == -1)
return make_error<Failure>(
"Failed to create shared memory object for memory definition: " +
Twine(strerror(errno)));
auto SharedMemoryFDClose =
make_scope_exit([SharedMemoryFD]() { close(SharedMemoryFD); });
if (ftruncate(SharedMemoryFD, MemVal.SizeBytes) != 0) {
return make_error<Failure>("Truncating a memory definiton failed: " +
Twine(strerror(errno)));
}
char *SharedMemoryMapping =
(char *)mmap(NULL, MemVal.SizeBytes, PROT_READ | PROT_WRITE, MAP_SHARED,
SharedMemoryFD, 0);
// fill the buffer with the specified value
size_t CurrentByte = 0;
const size_t ValueWidthBytes = MemVal.Value.getBitWidth() / 8;
while (CurrentByte < MemVal.SizeBytes - ValueWidthBytes) {
memcpy(SharedMemoryMapping + CurrentByte, MemVal.Value.getRawData(),
ValueWidthBytes);
CurrentByte += ValueWidthBytes;
}
// fill the last section
memcpy(SharedMemoryMapping + CurrentByte, MemVal.Value.getRawData(),
MemVal.SizeBytes - CurrentByte);
if (munmap(SharedMemoryMapping, MemVal.SizeBytes) != 0) {
return make_error<Failure>(
"Unmapping a memory definition in the parent failed: " +
Twine(strerror(errno)));
}
}
return Error::success();
}
Expected<int> SubprocessMemory::setupAuxiliaryMemoryInSubprocess(
std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
pid_t ParentPID, long ParentTID, int CounterFileDescriptor) {
std::string AuxiliaryMemoryName =
formatv("/{0}auxmem{1}", ParentTID, ParentPID);
int AuxiliaryMemoryFileDescriptor =
shm_open(AuxiliaryMemoryName.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
if (AuxiliaryMemoryFileDescriptor == -1)
return make_error<Failure>(
"Getting file descriptor for auxiliary memory failed: " +
Twine(strerror(errno)));
// set up memory value file descriptors
int *AuxiliaryMemoryMapping =
(int *)mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED,
AuxiliaryMemoryFileDescriptor, 0);
if ((intptr_t)AuxiliaryMemoryMapping == -1)
return make_error<Failure>("Mapping auxiliary memory failed");
AuxiliaryMemoryMapping[0] = CounterFileDescriptor;
for (auto &[Name, MemVal] : MemoryDefinitions) {
std::string MemoryValueName =
formatv("/{0}t{1}memdef{2}", ParentPID, ParentTID, MemVal.Index);
AuxiliaryMemoryMapping[AuxiliaryMemoryOffset + MemVal.Index] =
shm_open(MemoryValueName.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
if (AuxiliaryMemoryMapping[AuxiliaryMemoryOffset + MemVal.Index] == -1)
return make_error<Failure>("Mapping shared memory failed");
}
if (munmap(AuxiliaryMemoryMapping, 4096) == -1)
return make_error<Failure>("Unmapping auxiliary memory failed");
return AuxiliaryMemoryFileDescriptor;
}
SubprocessMemory::~SubprocessMemory() {
for (const std::string &SharedMemoryName : SharedMemoryNames) {
if (shm_unlink(SharedMemoryName.c_str()) != 0) {
errs() << "Failed to unlink shared memory section: " << strerror(errno)
<< "\n";
}
}
}
#else
Error SubprocessMemory::initializeSubprocessMemory(pid_t ProcessPID) {
return make_error<Failure>(
"initializeSubprocessMemory is only supported on Linux");
}
Error SubprocessMemory::addMemoryDefinition(
std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
pid_t ProcessPID) {
return make_error<Failure>("addMemoryDefinitions is only supported on Linux");
}
Expected<int> SubprocessMemory::setupAuxiliaryMemoryInSubprocess(
std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
pid_t ParentPID, long ParentTID, int CounterFileDescriptor) {
return make_error<Failure>(
"setupAuxiliaryMemoryInSubprocess is only supported on Linux");
}
SubprocessMemory::~SubprocessMemory() {}
#endif // !defined(__ANDROID__)
#endif // defined(__linux__)
} // namespace exegesis
} // namespace llvm
|