Skip to content

Commit 17ddd73

Browse files
scramsbymeta-codesync[bot]
authored andcommitted
Teach MemoryMapping to size Android ashmem fds
Summary: ### THIS DIFF Teach `folly::MemoryMapping` to size Android ashmem-backed file descriptors transparently by probing `ASharedMemory_getSize(fd)` before falling back to `fstat()`, and add coverage for that path. ### PLAN 1. Add legacy regular-file coverage. 2. Teach `folly::MemoryMapping` to size Android shared-memory fds transparently. 3. Migrate Codec Avatar back to `folly::MemoryMapping` and delete `SharedMemoryMapping`. ### CONTEXT Codec Avatar currently needs a custom Android-only mapping path because ashmem-backed render asset fds report an unusable size through the existing Folly path. This diff fixes that gap in Folly without adding a new caller-visible API and preserves the existing regular-file behavior when the ashmem probe does not apply. Differential Revision: D99928672 fbshipit-source-id: a86432e7c380983a053bdd1430bde2686960c651
1 parent 6da669e commit 17ddd73

4 files changed

Lines changed: 123 additions & 21 deletions

File tree

‎folly/system/MemoryMapping.cpp‎

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
#include <folly/portability/SysSyscall.h>
3434
#include <folly/portability/Unistd.h>
3535

36+
#ifdef __ANDROID__
37+
#include <dlfcn.h>
38+
#include <sys/ioctl.h>
39+
#endif
40+
3641
static constexpr ssize_t kDefaultMlockChunkSize = !folly::kMscVer
3742
// Linux implementations of unmap/mlock/munlock take a kernel
3843
// semaphore and block other threads from doing other memory
@@ -60,6 +65,20 @@ enum mmap_flags : int {
6065
#endif
6166
};
6267

68+
#ifdef __ANDROID__
69+
constexpr unsigned long kAshmemGetSizeIoctl = 0x00007704; // _IO(0x77, 4)
70+
71+
off64_t getAndroidSharedMemorySize(int fd) {
72+
using GetSizeFn = size_t (*)(int);
73+
static const auto getSize =
74+
reinterpret_cast<GetSizeFn>(dlsym(RTLD_DEFAULT, "ASharedMemory_getSize"));
75+
const auto size = getSize != nullptr
76+
? getSize(fd)
77+
: static_cast<size_t>(ioctl(fd, kAshmemGetSizeIoctl, 0));
78+
return size > 0 ? static_cast<off64_t>(size) : -1;
79+
}
80+
#endif
81+
6382
} // namespace
6483

6584
MemoryMapping::MemoryMapping(MemoryMapping&& other) noexcept {
@@ -102,6 +121,28 @@ void getDeviceOptions(dev_t device, off64_t& pageSize, bool& autoExtend) {
102121
}
103122
}
104123

124+
off64_t getFileSize(File const& file, off64_t& pageSize, bool& autoExtend) {
125+
#ifdef _WIN32
126+
// stat that support files larger then 4Gb
127+
struct _stat64 st;
128+
CHECK_ERR(_fstat64(file.fd(), &st));
129+
#else
130+
struct stat st{};
131+
CHECK_ERR(fstat(file.fd(), &st));
132+
#endif
133+
134+
if (pageSize == 0) {
135+
getDeviceOptions(st.st_dev, pageSize, autoExtend);
136+
}
137+
#ifdef __ANDROID__
138+
const auto fileSize = getAndroidSharedMemorySize(file.fd());
139+
if (fileSize >= 0) {
140+
return fileSize;
141+
}
142+
#endif
143+
return st.st_size;
144+
}
145+
105146
} // namespace
106147

107148
void MemoryMapping::init(off64_t offset, off64_t length) {
@@ -111,28 +152,14 @@ void MemoryMapping::init(off64_t offset, off64_t length) {
111152

112153
off64_t& pageSize = options_.pageSize;
113154

114-
#ifdef _WIN32
115-
// stat that support files larger then 4Gb
116-
struct _stat64 st;
117-
#else
118-
struct stat st;
119-
#endif
120-
121155
// On Linux, hugetlbfs file systems don't require ftruncate() to grow the
122156
// file, and (on kernels before 2.6.24) don't even allow it. Also, the file
123157
// size is always a multiple of the page size.
124158
bool autoExtend = false;
159+
off64_t fileSize = 0;
125160

126161
if (!anon) {
127-
// Stat the file
128-
#ifdef _WIN32
129-
CHECK_ERR(_fstat64(file_.fd(), &st));
130-
#else
131-
CHECK_ERR(fstat(file_.fd(), &st));
132-
#endif
133-
if (pageSize == 0) {
134-
getDeviceOptions(st.st_dev, pageSize, autoExtend);
135-
}
162+
fileSize = getFileSize(file_, pageSize, autoExtend);
136163
} else {
137164
DCHECK(!file_);
138165
DCHECK_EQ(offset, 0);
@@ -160,7 +187,7 @@ void MemoryMapping::init(off64_t offset, off64_t length) {
160187
mapLength_ = (mapLength_ + pageSize - 1) / pageSize * pageSize;
161188
}
162189

163-
off64_t remaining = anon ? length : st.st_size - offset;
190+
off64_t remaining = anon ? length : fileSize - offset;
164191

165192
if (mapLength_ == -1) {
166193
length = mapLength_ = remaining;
@@ -313,7 +340,7 @@ bool MemoryMapping::mlock(LockMode mode, LockFlags flags) {
313340
// If no flags are set, mlock2() behaves exactly the same as
314341
// mlock(). Prefer the portable variant.
315342
return !flags.lockOnFault
316-
? ::mlock(addr, len)
343+
? ::mlock(addr, len) // NOLINT(facebook-hte-BadCall-mlock)
317344
: mlock2wrapper(addr, len, flags);
318345
},
319346
mapStart_,
@@ -413,7 +440,7 @@ void MemoryMapping::advise(int advice, size_t offset, size_t length) const {
413440
PLOG_IF(WARNING, ::madvise(mapStart, length, advice)) << "madvise";
414441
}
415442

416-
MemoryMapping& MemoryMapping::operator=(MemoryMapping&& other) {
443+
MemoryMapping& MemoryMapping::operator=(MemoryMapping&& other) noexcept {
417444
swap(other);
418445
return *this;
419446
}

‎folly/system/MemoryMapping.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ class MemoryMapping {
176176
~MemoryMapping();
177177

178178
MemoryMapping& operator=(const MemoryMapping&) = delete;
179-
MemoryMapping& operator=(MemoryMapping&&);
179+
MemoryMapping& operator=(MemoryMapping&&) noexcept;
180180

181181
void swap(MemoryMapping& other) noexcept;
182182

‎folly/system/test/BUCK‎

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,46 @@
11
load("@fbsource//tools/build_defs:fb_xplat_cxx_test.bzl", "fb_xplat_cxx_test")
22
load("@fbsource//tools/build_defs:platform_defs.bzl", "ANDROID", "CXX", "FBCODE")
33
load("@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_unittest.bzl", "fb_dirsync_cpp_unittest")
4+
load("@fbsource//tools/target_determinator/macros:ci.bzl", "ci")
45
load("@fbsource//xplat/pfh/triage_InfrastructureSupermoduleOptou:DEFS.bzl", "triage_InfrastructureSupermoduleOptou")
56

7+
# Android API level gate for ashmem tests. ASharedMemory_create() requires
8+
# API 26+. FOLLY_TEST_HAS_ASHMEM is passed as a compiler flag so the C++
9+
# source does not duplicate the threshold.
10+
_ASHMEM_FLAG = ["-DFOLLY_TEST_HAS_ASHMEM=1"]
11+
12+
_ASHMEM_TOOLCHAIN = ["fbsource//third-party/toolchains:android"]
13+
14+
_ASHMEM_COMPILER_FLAGS = select({
15+
"DEFAULT": [],
16+
"ovr_config//os/version/android:api-level-26": _ASHMEM_FLAG,
17+
"ovr_config//os/version/android:api-level-27": _ASHMEM_FLAG,
18+
"ovr_config//os/version/android:api-level-28": _ASHMEM_FLAG,
19+
"ovr_config//os/version/android:api-level-29": _ASHMEM_FLAG,
20+
"ovr_config//os/version/android:api-level-30": _ASHMEM_FLAG,
21+
"ovr_config//os/version/android:api-level-31": _ASHMEM_FLAG,
22+
"ovr_config//os/version/android:api-level-32": _ASHMEM_FLAG,
23+
"ovr_config//os/version/android:api-level-33": _ASHMEM_FLAG,
24+
"ovr_config//os/version/android:api-level-34": _ASHMEM_FLAG,
25+
"ovr_config//os/version/android:api-level-35": _ASHMEM_FLAG,
26+
"ovr_config//os/version/android:api-level-36": _ASHMEM_FLAG,
27+
})
28+
29+
_ASHMEM_DEPS = select({
30+
"DEFAULT": [],
31+
"ovr_config//os/version/android:api-level-26": _ASHMEM_TOOLCHAIN,
32+
"ovr_config//os/version/android:api-level-27": _ASHMEM_TOOLCHAIN,
33+
"ovr_config//os/version/android:api-level-28": _ASHMEM_TOOLCHAIN,
34+
"ovr_config//os/version/android:api-level-29": _ASHMEM_TOOLCHAIN,
35+
"ovr_config//os/version/android:api-level-30": _ASHMEM_TOOLCHAIN,
36+
"ovr_config//os/version/android:api-level-31": _ASHMEM_TOOLCHAIN,
37+
"ovr_config//os/version/android:api-level-32": _ASHMEM_TOOLCHAIN,
38+
"ovr_config//os/version/android:api-level-33": _ASHMEM_TOOLCHAIN,
39+
"ovr_config//os/version/android:api-level-34": _ASHMEM_TOOLCHAIN,
40+
"ovr_config//os/version/android:api-level-35": _ASHMEM_TOOLCHAIN,
41+
"ovr_config//os/version/android:api-level-36": _ASHMEM_TOOLCHAIN,
42+
})
43+
644
oncall("fbcode_entropy_wardens_folly")
745

846
fb_dirsync_cpp_unittest(
@@ -61,20 +99,26 @@ fb_dirsync_cpp_unittest(
6199
fb_dirsync_cpp_unittest(
62100
name = "memory_mapping_test",
63101
srcs = ["MemoryMappingTest.cpp"],
102+
compiler_flags = _ASHMEM_COMPILER_FLAGS,
64103
feature = triage_InfrastructureSupermoduleOptou,
65104
platforms = (ANDROID, CXX, FBCODE),
66105
use_instrumentation_test = True,
67106
# TODO(T263893609): Drop this explicit override once folly_xplat_cxx_test
68107
# forwards Android test attrs correctly for xplat/folly tests.
69108
xplat_impl = fb_xplat_cxx_test,
109+
# Run with arvr Android API 34 mode so the ashmem test (gated on
110+
# __ANDROID_API__ >= _ASHMEM_MIN_API) compiles and executes in CI.
111+
xplat_labels = ci.labels(
112+
ci.linux(ci.mode("fbsource//arvr/mode/android/apk/linux/dev")),
113+
),
70114
deps = [
71115
"fbsource//third-party/glog:glog",
72116
"//folly:file_util",
73117
"//folly:random",
74118
"//folly/portability:gtest",
75119
"//folly/portability:sys_mman",
76120
"//folly/system:memory_mapping",
77-
],
121+
] + _ASHMEM_DEPS,
78122
)
79123

80124
fb_dirsync_cpp_unittest(

‎folly/system/test/MemoryMappingTest.cpp‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@
2525
#include <folly/portability/GTest.h>
2626
#include <folly/portability/SysMman.h>
2727

28+
// FOLLY_TEST_HAS_ASHMEM is defined by Buck (compiler_flags) when the Android
29+
// API level is high enough for ASharedMemory_create(). Default to 0 so the
30+
// test compiles on non-Android and on older Android API levels.
31+
#ifndef FOLLY_TEST_HAS_ASHMEM
32+
#define FOLLY_TEST_HAS_ASHMEM 0
33+
#endif
34+
35+
#if FOLLY_TEST_HAS_ASHMEM
36+
#include <algorithm>
37+
#include <android/sharedmem.h>
38+
#endif
39+
2840
static constexpr double kSomeDouble = 3.14;
2941

3042
namespace folly {
@@ -176,6 +188,25 @@ TEST(MemoryMapping, WritableMappingGrowsRegularFileToRequestedLength) {
176188
EXPECT_EQ('!', m.data()[7]);
177189
}
178190

191+
#if FOLLY_TEST_HAS_ASHMEM
192+
TEST(MemoryMapping, ExplicitLengthUsesAshmemSize) {
193+
constexpr size_t kSize = 8;
194+
folly::File f(ASharedMemory_create("folly-memory-mapping-test", kSize), true);
195+
ASSERT_TRUE(f);
196+
197+
void* const address =
198+
mmap(nullptr, kSize, PROT_READ | PROT_WRITE, MAP_SHARED, f.fd(), 0);
199+
ASSERT_NE(MAP_FAILED, address);
200+
const StringPiece kData = "ashmem!!";
201+
std::copy_n(kData.data(), kData.size(), static_cast<char*>(address));
202+
ASSERT_EQ(0, munmap(address, kSize));
203+
204+
MemoryMapping m(File(f.fd()), 0, 99);
205+
EXPECT_EQ(kSize, m.data().size());
206+
EXPECT_EQ(kData, m.data().subpiece(0, kSize));
207+
}
208+
#endif
209+
179210
TEST(MemoryMapping, ZeroLength) {
180211
File f = File::temporary();
181212
MemoryMapping m(File(f.fd()));

0 commit comments

Comments
 (0)