Home | History | Annotate | Download | only in perfprofd
      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:
     46   explicit FileCopyingOutputStream(android::base::unique_fd&& fd_in) : fd_(std::move(fd_in)) {
     47   };
     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 
     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 
    104 GzipOutputStream::~GzipOutputStream() {
    105   if (stream_ != nullptr) {
    106     deflateEnd(stream_);
    107     delete stream_;
    108     stream_ = nullptr;
    109   }
    110 }
    111 
    112 bool GzipOutputStream::WriteAliasedRaw(const void* data ATTRIBUTE_UNUSED,
    113                                        int size ATTRIBUTE_UNUSED) {
    114   LOG(FATAL) << "Not supported";
    115   __builtin_unreachable();
    116 }
    117 bool GzipOutputStream::AllowsAliasing() const {
    118   return false;
    119 }
    120 
    121 google::protobuf::int64 GzipOutputStream::ByteCount() const {
    122   return stream_->total_in + stream_->avail_in;
    123 }
    124 
    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 
    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 
    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 
    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 
    221 void GzipOutputStream::BackUp(int count) {
    222   CHECK_GE(stream_->avail_in, count);
    223   stream_->avail_in -= count;
    224 }
    225 
    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 
    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 
    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 
    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