/* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "perfetto/ext/base/scoped_mmap.h" #include #include "perfetto/ext/base/file_utils.h" #include "perfetto/ext/base/scoped_file.h" #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) #include #include #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) #include #endif namespace perfetto::base { namespace { ScopedPlatformHandle OpenFileForMmap(const char* fname) { #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) return OpenFile(fname, O_RDONLY); #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) // This does not use base::OpenFile to avoid getting an exclusive lock. return ScopedPlatformHandle(CreateFileA(fname, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)); #else // mmap is not supported. Do not even open the file. base::ignore_result(fname); return ScopedPlatformHandle(); #endif } } // namespace ScopedMmap::ScopedMmap(ScopedMmap&& other) noexcept { *this = std::move(other); } ScopedMmap& ScopedMmap::operator=(ScopedMmap&& other) noexcept { if (this == &other) { return *this; } reset(); std::swap(ptr_, other.ptr_); std::swap(length_, other.length_); std::swap(file_, other.file_); #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) std::swap(map_, other.map_); #endif return *this; } ScopedMmap::~ScopedMmap() { reset(); } // static ScopedMmap ScopedMmap::FromHandle(base::ScopedPlatformHandle file, size_t length) { ScopedMmap ret; if (!file) { return ret; } #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) void* ptr = mmap(nullptr, length, PROT_READ, MAP_PRIVATE, *file, 0); if (ptr != MAP_FAILED) { ret.ptr_ = ptr; ret.length_ = length; ret.file_ = std::move(file); } #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) ScopedPlatformHandle map( CreateFileMapping(*file, nullptr, PAGE_READONLY, 0, 0, nullptr)); if (!map) { return ret; } void* ptr = MapViewOfFile(*map, FILE_MAP_READ, 0, 0, length); if (ptr != nullptr) { ret.ptr_ = ptr; ret.length_ = length; ret.file_ = std::move(file); ret.map_ = std::move(map); } #else base::ignore_result(length); #endif return ret; } bool ScopedMmap::reset() noexcept { bool ret = true; #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) if (ptr_ != nullptr) { ret = munmap(ptr_, length_) == 0; } #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) if (ptr_ != nullptr) { ret = UnmapViewOfFile(ptr_); } map_.reset(); #endif ptr_ = nullptr; length_ = 0; file_.reset(); return ret; } #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) // static ScopedMmap ScopedMmap::InheritMmappedRange(void* data, size_t size) { ScopedMmap ret; ret.ptr_ = data; ret.length_ = size; return ret; } #endif ScopedMmap ReadMmapFilePart(const char* fname, size_t length) { return ScopedMmap::FromHandle(OpenFileForMmap(fname), length); } ScopedMmap ReadMmapWholeFile(const char* fname) { ScopedPlatformHandle file = OpenFileForMmap(fname); if (!file) { return ScopedMmap(); } std::optional file_size = GetFileSize(file.get()); if (!file_size.has_value()) { return ScopedMmap(); } size_t size = static_cast(*file_size); if (static_cast(size) != *file_size) { return ScopedMmap(); } return ScopedMmap::FromHandle(std::move(file), size); } } // namespace perfetto::base