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 (reinterpret_cast<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
|