• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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