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