1 /*
2 **
3 ** Copyright 2015, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 ** http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17
18 #include "perfprofd_io.h"
19
20 #include <fcntl.h>
21 #include <unistd.h>
22
23 #include <memory>
24
25 #include <android-base/file.h>
26 #include <android-base/logging.h>
27 #include <android-base/macros.h>
28 #include <android-base/stringprintf.h>
29 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
30 #include <zlib.h>
31
32 #include "perfprofd_record.pb.h"
33
34 namespace android {
35 namespace perfprofd {
36
37 using android::base::StringPrintf;
38 using android::base::unique_fd;
39 using android::base::WriteFully;
40
41 namespace {
42
43 // Protobuf's file implementation is not available in protobuf-lite. :-(
44 class FileCopyingOutputStream : public ::google::protobuf::io::CopyingOutputStream {
45 public:
FileCopyingOutputStream(android::base::unique_fd && fd_in)46 explicit FileCopyingOutputStream(android::base::unique_fd&& fd_in) : fd_(std::move(fd_in)) {
47 };
Write(const void * buffer,int size)48 bool Write(const void * buffer, int size) override {
49 return WriteFully(fd_.get(), buffer, size);
50 }
51
52 private:
53 android::base::unique_fd fd_;
54 };
55
56 using google::protobuf::io::ZeroCopyOutputStream;
57
58 // Protobuf's Gzip implementation is not available in protobuf-lite. :-(
59 class GzipOutputStream : public ZeroCopyOutputStream {
60 public:
61 ~GzipOutputStream();
62
63 static std::unique_ptr<GzipOutputStream> Create(ZeroCopyOutputStream* next,
64 std::string* error_msg);
65
66 bool Next(void** data, int* size) override;
67
68 void BackUp(int count) override;
69
70 google::protobuf::int64 ByteCount() const override;
71
72 bool WriteAliasedRaw(const void* data, int size) override;
73 bool AllowsAliasing() const override;
74
75 bool Flush();
76 bool Close();
77
78 private:
79 GzipOutputStream(ZeroCopyOutputStream* next, z_stream* stream);
80
81 int Write(int flush_flags);
82 bool NextBuffer();
83
84 ZeroCopyOutputStream* next_;
85 void* next_data_;
86 int next_size_;
87
88 z_stream* stream_;
89 std::unique_ptr<uint8_t[]> stream_buffer_;
90 bool had_error_;
91 };
92
93 constexpr size_t kStreamBufferSize = 16u * 1024u;
94
GzipOutputStream(ZeroCopyOutputStream * next,z_stream * stream)95 GzipOutputStream::GzipOutputStream(ZeroCopyOutputStream* next, z_stream* stream)
96 : next_(next),
97 next_data_(nullptr),
98 next_size_(0),
99 stream_(stream),
100 stream_buffer_(nullptr),
101 had_error_(false) {
102 }
103
~GzipOutputStream()104 GzipOutputStream::~GzipOutputStream() {
105 if (stream_ != nullptr) {
106 deflateEnd(stream_);
107 delete stream_;
108 stream_ = nullptr;
109 }
110 }
111
WriteAliasedRaw(const void * data ATTRIBUTE_UNUSED,int size ATTRIBUTE_UNUSED)112 bool GzipOutputStream::WriteAliasedRaw(const void* data ATTRIBUTE_UNUSED,
113 int size ATTRIBUTE_UNUSED) {
114 LOG(FATAL) << "Not supported";
115 __builtin_unreachable();
116 }
AllowsAliasing() const117 bool GzipOutputStream::AllowsAliasing() const {
118 return false;
119 }
120
ByteCount() const121 google::protobuf::int64 GzipOutputStream::ByteCount() const {
122 return stream_->total_in + stream_->avail_in;
123 }
124
Create(ZeroCopyOutputStream * next,std::string * error_msg)125 std::unique_ptr<GzipOutputStream> GzipOutputStream::Create(ZeroCopyOutputStream* next,
126 std::string* error_msg) {
127 std::unique_ptr<z_stream> stream(new z_stream);
128
129 stream->zalloc = Z_NULL;
130 stream->zfree = Z_NULL;
131 stream->opaque = Z_NULL;
132 stream->msg = nullptr;
133 stream->avail_in = 0;
134 stream->total_in = 0;
135 stream->next_in = nullptr;
136 stream->total_out = 0;
137
138 {
139 constexpr int kWindowBits = 15;
140 constexpr int kGzipEncoding = 16;
141 constexpr int kMemLevel = 8; // Default.
142 int init_result = deflateInit2(stream.get(),
143 Z_DEFAULT_COMPRESSION,
144 Z_DEFLATED,
145 kWindowBits | kGzipEncoding,
146 kMemLevel,
147 Z_DEFAULT_STRATEGY);
148 if (init_result != Z_OK) {
149 *error_msg = StringPrintf("Could not initialize compression: %d (%s)",
150 init_result,
151 stream->msg != nullptr ? stream->msg : "no message");
152 return nullptr;
153 }
154 }
155
156 return std::unique_ptr<GzipOutputStream>(new GzipOutputStream(next, stream.release()));
157 }
158
NextBuffer()159 bool GzipOutputStream::NextBuffer() {
160 for (;;) {
161 if (!next_->Next(&next_data_, &next_size_)) {
162 next_data_ = nullptr;
163 next_size_ = 0;
164 return false;
165 }
166 if (next_size_ == 0) {
167 continue;
168 }
169 stream_->next_out = static_cast<Bytef*>(next_data_);
170 stream_->avail_out = next_size_;
171 return true;
172 }
173 }
174
Write(int flush_flags)175 int GzipOutputStream::Write(int flush_flags) {
176 CHECK(flush_flags == Z_NO_FLUSH || flush_flags == Z_FULL_FLUSH || flush_flags == Z_FINISH);
177
178 int res;
179 do {
180 if ((next_data_ == nullptr || stream_->avail_out == 0) && !NextBuffer()) {
181 return Z_BUF_ERROR;
182 }
183 res = deflate(stream_, flush_flags);
184 } while (res == Z_OK && stream_->avail_out == 0);
185
186 if (flush_flags == Z_FULL_FLUSH || flush_flags == Z_FINISH) {
187 next_->BackUp(stream_->avail_out);
188 next_data_ = nullptr;
189 next_size_ = 0;
190 }
191
192 return res;
193 }
194
Next(void ** data,int * size)195 bool GzipOutputStream::Next(void** data, int* size) {
196 if (had_error_) {
197 return false;
198 }
199
200 // Write all pending data.
201 if (stream_->avail_in > 0) {
202 int write_error = Write(Z_NO_FLUSH);
203 if (write_error != Z_OK) {
204 had_error_ = true;
205 return false;
206 }
207 CHECK_EQ(stream_->avail_in, 0);
208 }
209
210 if (stream_buffer_ == nullptr) {
211 stream_buffer_.reset(new uint8_t[kStreamBufferSize]);
212 }
213
214 stream_->next_in = static_cast<Bytef*>(stream_buffer_.get());
215 stream_->avail_in = kStreamBufferSize;
216 *data = stream_buffer_.get();
217 *size = kStreamBufferSize;
218 return true;
219 }
220
BackUp(int count)221 void GzipOutputStream::BackUp(int count) {
222 CHECK_GE(stream_->avail_in, count);
223 stream_->avail_in -= count;
224 }
225
Flush()226 bool GzipOutputStream::Flush() {
227 if (had_error_) {
228 return false;
229 }
230
231 int res = Write(Z_FULL_FLUSH);
232 had_error_ |= (res != Z_OK)
233 && !(res == Z_BUF_ERROR && stream_->avail_in == 0 && stream_->avail_out > 0);
234 return !had_error_;
235 }
236
Close()237 bool GzipOutputStream::Close() {
238 if (had_error_) {
239 return false;
240 }
241
242 {
243 int res;
244 do {
245 res = Write(Z_FINISH);
246 } while (res == Z_OK);
247 }
248
249 int res = deflateEnd(stream_);
250 delete stream_;
251 stream_ = nullptr;
252
253 had_error_ = true; // Pretend an error so no other operations succeed.
254
255 return res == Z_OK;
256 }
257
258 } // namespace
259
SerializeProtobuf(android::perfprofd::PerfprofdRecord * encodedProfile,android::base::unique_fd && fd,bool compress)260 bool SerializeProtobuf(android::perfprofd::PerfprofdRecord* encodedProfile,
261 android::base::unique_fd&& fd,
262 bool compress) {
263 FileCopyingOutputStream fcos(std::move(fd));
264 google::protobuf::io::CopyingOutputStreamAdaptor cosa(&fcos);
265
266 ZeroCopyOutputStream* out;
267
268 std::unique_ptr<GzipOutputStream> gzip;
269 if (compress) {
270 std::string error_msg;
271 gzip = GzipOutputStream::Create(&cosa, &error_msg);
272 if (gzip == nullptr) {
273 LOG(ERROR) << error_msg;
274 return false;
275 }
276 out = gzip.get();
277 } else {
278 out = &cosa;
279 }
280
281 bool serialized = encodedProfile->SerializeToZeroCopyStream(out);
282 if (!serialized) {
283 LOG(WARNING) << "SerializeToZeroCopyStream failed";
284 return false;
285 }
286
287 bool zip_ok = true;
288 if (gzip != nullptr) {
289 zip_ok = gzip->Flush();
290 zip_ok = gzip->Close() && zip_ok;
291 }
292 cosa.Flush();
293 return zip_ok;
294 }
295
SerializeProtobuf(PerfprofdRecord * encodedProfile,const char * encoded_file_path,bool compress)296 bool SerializeProtobuf(PerfprofdRecord* encodedProfile,
297 const char* encoded_file_path,
298 bool compress) {
299 unlink(encoded_file_path); // Attempt to unlink for a clean slate.
300 constexpr int kFlags = O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC;
301 unique_fd fd(open(encoded_file_path, kFlags, 0664));
302 if (fd.get() == -1) {
303 PLOG(WARNING) << "Could not open " << encoded_file_path << " for serialization";
304 return false;
305 }
306 return SerializeProtobuf(encodedProfile, std::move(fd), compress);
307 }
308
309 } // namespace perfprofd
310 } // namespace android
311