1 // Copyright (C) 2017 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "protobuf_io.h"
16
17 #include "common/trace.h"
18 #include "serialize/arena_ptr.h"
19
20 #include <android-base/chrono_utils.h>
21 #include <android-base/logging.h>
22 #include <android-base/unique_fd.h>
23 #include <fcntl.h>
24 #include <sys/mman.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <utils/Trace.h>
29
30 #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
31 #include "system/iorap/src/serialize/TraceFile.pb.h"
32
33 namespace iorap {
34 namespace serialize {
35
Open(std::string file_path)36 ArenaPtr<proto::TraceFile> ProtobufIO::Open(std::string file_path) {
37 // TODO: file a bug about this.
38 // Note: can't use {} here, clang think it's narrowing from long->int.
39 android::base::unique_fd fd(TEMP_FAILURE_RETRY(::open(file_path.c_str(), O_RDONLY)));
40 if (fd.get() < 0) {
41 PLOG(DEBUG) << "ProtobufIO: open failed: " << file_path;
42 return nullptr;
43 }
44
45 return Open(fd.get(), file_path.c_str());
46 }
47
Open(int fd,const char * file_path)48 ArenaPtr<proto::TraceFile> ProtobufIO::Open(int fd, const char* file_path) {
49
50 ScopedFormatTrace atrace_protobuf_io_open(ATRACE_TAG_ACTIVITY_MANAGER,
51 "ProtobufIO::Open %s",
52 file_path);
53 android::base::Timer timer{};
54
55 struct stat buf;
56 if (fstat(fd, /*out*/&buf) < 0) {
57 PLOG(ERROR) << "ProtobufIO: open error, fstat failed: " << file_path;
58 return nullptr;
59 }
60 // XX: off64_t for stat::st_size ?
61
62 // Using the mmap appears to be the only way to do zero-copy with protobuf lite.
63 void* data = mmap(/*addr*/nullptr,
64 buf.st_size,
65 PROT_READ, MAP_SHARED | MAP_POPULATE,
66 fd,
67 /*offset*/0);
68 if (data == nullptr) {
69 PLOG(ERROR) << "ProtobufIO: open error, mmap failed: " << file_path;
70 return nullptr;
71 }
72
73 ArenaPtr<proto::TraceFile> protobuf_trace_file = ArenaPtr<proto::TraceFile>::Make();
74 if (protobuf_trace_file == nullptr) {
75 LOG(ERROR) << "ProtobufIO: open error, failed to create arena: " << file_path;
76 return nullptr;
77 }
78
79 google::protobuf::io::ArrayInputStream protobuf_input_stream{data, static_cast<int>(buf.st_size)};
80 if (!protobuf_trace_file->ParseFromZeroCopyStream(/*in*/&protobuf_input_stream)) {
81 // XX: Does protobuf on android already have the right LogHandler ?
82 LOG(ERROR) << "ProtobufIO: open error, protobuf parsing failed: " << file_path;
83 return nullptr;
84 }
85
86 if (munmap(data, buf.st_size) < 0) {
87 PLOG(WARNING) << "ProtobufIO: open problem, munmap failed, possibly memory leak? "
88 << file_path;
89 }
90
91 LOG(VERBOSE) << "ProtobufIO: open succeeded: " << file_path << ", duration: " << timer;
92 return protobuf_trace_file;
93 }
94
WriteFully(const::google::protobuf::MessageLite & message,std::string_view file_path)95 iorap::expected<size_t /*bytes written*/, int /*errno*/> ProtobufIO::WriteFully(
96 const ::google::protobuf::MessageLite& message,
97 std::string_view file_path) {
98
99 std::string str{file_path};
100 android::base::unique_fd fd(TEMP_FAILURE_RETRY(
101 ::open(str.c_str(),
102 O_CREAT | O_TRUNC | O_RDWR,
103 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP))); // ugo: rw-rw----
104 if (fd.get() < 0) {
105 int err = errno;
106 PLOG(ERROR) << "ProtobufIO: open failed: " << file_path;
107 return unexpected{err};
108 }
109
110 return WriteFully(message, fd.get(), file_path);
111 }
112
WriteFully(const::google::protobuf::MessageLite & message,int fd,std::string_view file_path)113 iorap::expected<size_t /*bytes written*/, int /*errno*/> ProtobufIO::WriteFully(
114 const ::google::protobuf::MessageLite& message,
115 int fd,
116 std::string_view file_path) {
117
118 int byte_size = message.ByteSize();
119 if (byte_size < 0) {
120 DCHECK(false) << "Invalid protobuf size: " << byte_size;
121 LOG(ERROR) << "ProtobufIO: Invalid protobuf size: " << byte_size;
122 return unexpected{EDOM};
123 }
124 size_t serialized_size = static_cast<size_t>(byte_size);
125
126 // Change the file to be exactly the length of the protobuf.
127 if (ftruncate(fd, static_cast<off_t>(serialized_size)) < 0) {
128 int err = errno;
129 PLOG(ERROR) << "ProtobufIO: ftruncate (size=" << serialized_size << ") failed";
130 return unexpected{err};
131 }
132
133 // Using the mmap appears to be the only way to do zero-copy with protobuf lite.
134 void* data = mmap(/*addr*/nullptr,
135 serialized_size,
136 PROT_WRITE,
137 MAP_SHARED,
138 fd,
139 /*offset*/0);
140 if (data == nullptr) {
141 int err = errno;
142 PLOG(ERROR) << "ProtobufIO: mmap failed: " << file_path;
143 return unexpected{err};
144 }
145
146 // Zero-copy write from protobuf to file via memory-map.
147 ::google::protobuf::io::ArrayOutputStream output_stream{data, byte_size};
148 if (!message.SerializeToZeroCopyStream(/*inout*/&output_stream)) {
149 // This should never happen since we pre-allocated the file and memory map to be large
150 // enough to store the full protobuf.
151 DCHECK(false) << "ProtobufIO:: SerializeToZeroCopyStream failed despite precalculating size";
152 LOG(ERROR) << "ProtobufIO: SerializeToZeroCopyStream failed";
153 return unexpected{EXFULL};
154 }
155
156 // Guarantee that changes are written back prior to munmap.
157 if (msync(data, static_cast<size_t>(serialized_size), MS_SYNC) < 0) {
158 int err = errno;
159 PLOG(ERROR) << "ProtobufIO: msync failed";
160 return unexpected{err};
161 }
162
163 if (munmap(data, serialized_size) < 0) {
164 PLOG(WARNING) << "ProtobufIO: munmap failed, possibly memory leak? "
165 << file_path;
166 }
167
168 return serialized_size;
169 }
170
171 } // namespace serialize
172 } // namespace iorap
173
174