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
23namespace llvm {
24namespace 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
37long 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
46Error 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(Fmt: "/{0}auxmem{1}", Vals: getCurrentTID(), Vals&: ProcessID);
54 int AuxiliaryMemoryFD = shm_open(name: AuxiliaryMemoryName.c_str(),
55 O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
56 if (AuxiliaryMemoryFD == -1)
57 return make_error<Failure>(
58 Args: "Failed to create shared memory object for auxiliary memory: " +
59 Twine(strerror(errno)));
60 auto AuxiliaryMemoryFDClose =
61 make_scope_exit(F: [AuxiliaryMemoryFD]() { close(fd: AuxiliaryMemoryFD); });
62 if (ftruncate(fd: AuxiliaryMemoryFD, length: AuxiliaryMemorySize) != 0) {
63 return make_error<Failure>(Args: "Truncating the auxiliary memory failed: " +
64 Twine(strerror(errno)));
65 }
66 SharedMemoryNames.push_back(x: AuxiliaryMemoryName);
67 return Error::success();
68}
69
70Error SubprocessMemory::addMemoryDefinition(
71 std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
72 pid_t ProcessPID) {
73 SharedMemoryNames.reserve(n: MemoryDefinitions.size());
74 for (auto &[Name, MemVal] : MemoryDefinitions) {
75 std::string SharedMemoryName =
76 formatv(Fmt: "/{0}t{1}memdef{2}", Vals&: ProcessPID, Vals: getCurrentTID(), Vals&: MemVal.Index);
77 SharedMemoryNames.push_back(x: SharedMemoryName);
78 int SharedMemoryFD =
79 shm_open(name: SharedMemoryName.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
80 if (SharedMemoryFD == -1)
81 return make_error<Failure>(
82 Args: "Failed to create shared memory object for memory definition: " +
83 Twine(strerror(errno)));
84 auto SharedMemoryFDClose =
85 make_scope_exit(F: [SharedMemoryFD]() { close(fd: SharedMemoryFD); });
86 if (ftruncate(fd: SharedMemoryFD, length: MemVal.SizeBytes) != 0) {
87 return make_error<Failure>(Args: "Truncating a memory definiton failed: " +
88 Twine(strerror(errno)));
89 }
90
91 char *SharedMemoryMapping =
92 (char *)mmap(NULL, len: MemVal.SizeBytes, PROT_READ | PROT_WRITE, MAP_SHARED,
93 fd: SharedMemoryFD, offset: 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(dest: SharedMemoryMapping + CurrentByte, src: MemVal.Value.getRawData(),
99 n: ValueWidthBytes);
100 CurrentByte += ValueWidthBytes;
101 }
102 // fill the last section
103 memcpy(dest: SharedMemoryMapping + CurrentByte, src: MemVal.Value.getRawData(),
104 n: MemVal.SizeBytes - CurrentByte);
105 if (munmap(addr: SharedMemoryMapping, len: MemVal.SizeBytes) != 0) {
106 return make_error<Failure>(
107 Args: "Unmapping a memory definition in the parent failed: " +
108 Twine(strerror(errno)));
109 }
110 }
111 return Error::success();
112}
113
114Expected<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(Fmt: "/{0}auxmem{1}", Vals&: ParentTID, Vals&: ParentPID);
119 int AuxiliaryMemoryFileDescriptor =
120 shm_open(name: AuxiliaryMemoryName.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
121 if (AuxiliaryMemoryFileDescriptor == -1)
122 return make_error<Failure>(
123 Args: "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, len: 4096, PROT_READ | PROT_WRITE, MAP_SHARED,
128 fd: AuxiliaryMemoryFileDescriptor, offset: 0);
129 if ((intptr_t)AuxiliaryMemoryMapping == -1)
130 return make_error<Failure>(Args: "Mapping auxiliary memory failed");
131 AuxiliaryMemoryMapping[0] = CounterFileDescriptor;
132 for (auto &[Name, MemVal] : MemoryDefinitions) {
133 std::string MemoryValueName =
134 formatv(Fmt: "/{0}t{1}memdef{2}", Vals&: ParentPID, Vals&: ParentTID, Vals&: MemVal.Index);
135 AuxiliaryMemoryMapping[AuxiliaryMemoryOffset + MemVal.Index] =
136 shm_open(name: MemoryValueName.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
137 if (AuxiliaryMemoryMapping[AuxiliaryMemoryOffset + MemVal.Index] == -1)
138 return make_error<Failure>(Args: "Mapping shared memory failed");
139 }
140 if (munmap(addr: AuxiliaryMemoryMapping, len: 4096) == -1)
141 return make_error<Failure>(Args: "Unmapping auxiliary memory failed");
142 return AuxiliaryMemoryFileDescriptor;
143}
144
145SubprocessMemory::~SubprocessMemory() {
146 for (const std::string &SharedMemoryName : SharedMemoryNames) {
147 if (shm_unlink(name: SharedMemoryName.c_str()) != 0) {
148 errs() << "Failed to unlink shared memory section: " << strerror(errno)
149 << "\n";
150 }
151 }
152}
153
154#else
155
156Error SubprocessMemory::initializeSubprocessMemory(pid_t ProcessPID) {
157 return make_error<Failure>(
158 "initializeSubprocessMemory is only supported on Linux");
159}
160
161Error 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
167Expected<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
174SubprocessMemory::~SubprocessMemory() {}
175
176#endif // !defined(__ANDROID__)
177#endif // defined(__linux__)
178
179} // namespace exegesis
180} // namespace llvm
181