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