xref: /llvm-project/llvm/tools/llvm-exegesis/lib/SubprocessMemory.cpp (revision fac87b889c50f493ba332950f613fa15a45be9a9)
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