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