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