blob: 03c0a5a02dbe8e239550ed3abd97f00c2fb3e7eb [file] [log] [blame]
// Copyright 2019 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/stream/log_output_stream.h"
#include <algorithm>
#include "base/logging.h"
#if defined(OS_ANDROID)
#include <android/log.h>
#endif
namespace crashpad {
namespace {
// Most minidumps are expected to be compressed and encoded into less than 128k.
constexpr size_t kOutputCap = 128 * 1024;
// From Android NDK r20 <android/log.h>, log message text may be truncated to
// less than an implementation-specific limit (1023 bytes), for sake of safe
// and being easy to read in logcat, choose 512.
constexpr size_t kLineBufferSize = 512;
} // namespace
LogOutputStream::LogOutputStream()
: output_count_(0), flush_needed_(false), flushed_(false) {
buffer_.reserve(kLineBufferSize);
}
LogOutputStream::~LogOutputStream() {
DCHECK(!flush_needed_);
}
bool LogOutputStream::Write(const uint8_t* data, size_t size) {
DCHECK(!flushed_);
flush_needed_ = true;
while (size > 0) {
size_t m = std::min(kLineBufferSize - buffer_.size(), size);
buffer_.append(reinterpret_cast<const char*>(data), m);
data += m;
size -= m;
if (buffer_.size() == kLineBufferSize && !WriteBuffer()) {
flush_needed_ = false;
LOG(ERROR) << "Write: exceeds cap.";
if (output_stream_for_testing_)
output_stream_for_testing_->Flush();
return false;
}
}
return true;
}
bool LogOutputStream::WriteBuffer() {
if (output_count_ == 0) {
if (!WriteToLog("-----BEGIN CRASHPAD MINIDUMP-----"))
return false;
}
if (buffer_.empty())
return true;
output_count_ += buffer_.size();
if (output_count_ > kOutputCap) {
WriteToLog("-----ABORT CRASHPAD MINIDUMP-----");
return false;
}
bool result = WriteToLog(buffer_.c_str());
buffer_.clear();
return result;
}
bool LogOutputStream::WriteToLog(const char* buf) {
#if defined(OS_ANDROID)
int ret =
__android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_FATAL, "crashpad", buf);
if (ret < 0) {
errno = -ret;
PLOG(ERROR) << "__android_log_buf_write";
return false;
}
#endif
// For testing.
if (output_stream_for_testing_) {
return output_stream_for_testing_->Write(
reinterpret_cast<const uint8_t*>(buf), strlen(buf));
}
return true;
}
bool LogOutputStream::Flush() {
flush_needed_ = false;
flushed_ = true;
bool result = true;
if (WriteBuffer()) {
result = WriteToLog("-----END CRASHPAD MINIDUMP-----");
} else {
LOG(ERROR) << "Flush: exceeds cap.";
result = false;
}
// Since output_stream_for_testing_'s Write() method has been called, its
// Flush() shall always be invoked.
if (output_stream_for_testing_)
output_stream_for_testing_->Flush();
return result;
}
void LogOutputStream::SetOutputStreamForTesting(
std::unique_ptr<OutputStreamInterface> stream) {
output_stream_for_testing_ = std::move(stream);
}
} // namespace crashpad