michael@0: // Copyright (c) 2006, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: // minidump_file_writer.cc: Minidump file writer implementation. michael@0: // michael@0: // See minidump_file_writer.h for documentation. michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "client/minidump_file_writer-inl.h" michael@0: #include "common/linux/linux_libc_support.h" michael@0: #include "common/string_conversion.h" michael@0: #if __linux__ michael@0: #include "third_party/lss/linux_syscall_support.h" michael@0: #endif michael@0: michael@0: namespace google_breakpad { michael@0: michael@0: const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast(-1); michael@0: michael@0: MinidumpFileWriter::MinidumpFileWriter() michael@0: : file_(-1), michael@0: close_file_when_destroyed_(true), michael@0: position_(0), michael@0: size_(0) { michael@0: } michael@0: michael@0: MinidumpFileWriter::~MinidumpFileWriter() { michael@0: if (close_file_when_destroyed_) michael@0: Close(); michael@0: } michael@0: michael@0: bool MinidumpFileWriter::Open(const char *path) { michael@0: assert(file_ == -1); michael@0: #if __linux__ michael@0: file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); michael@0: #else michael@0: file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); michael@0: #endif michael@0: michael@0: return file_ != -1; michael@0: } michael@0: michael@0: void MinidumpFileWriter::SetFile(const int file) { michael@0: assert(file_ == -1); michael@0: file_ = file; michael@0: close_file_when_destroyed_ = false; michael@0: } michael@0: michael@0: bool MinidumpFileWriter::Close() { michael@0: bool result = true; michael@0: michael@0: if (file_ != -1) { michael@0: if (-1 == ftruncate(file_, position_)) { michael@0: return false; michael@0: } michael@0: #if __linux__ michael@0: result = (sys_close(file_) == 0); michael@0: #else michael@0: result = (close(file_) == 0); michael@0: #endif michael@0: file_ = -1; michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str, michael@0: unsigned int length, michael@0: TypedMDRVA *mdstring) { michael@0: bool result = true; michael@0: if (sizeof(wchar_t) == sizeof(uint16_t)) { michael@0: // Shortcut if wchar_t is the same size as MDString's buffer michael@0: result = mdstring->Copy(str, mdstring->get()->length); michael@0: } else { michael@0: uint16_t out[2]; michael@0: int out_idx = 0; michael@0: michael@0: // Copy the string character by character michael@0: while (length && result) { michael@0: UTF32ToUTF16Char(*str, out); michael@0: if (!out[0]) michael@0: return false; michael@0: michael@0: // Process one character at a time michael@0: --length; michael@0: ++str; michael@0: michael@0: // Append the one or two UTF-16 characters. The first one will be non- michael@0: // zero, but the second one may be zero, depending on the conversion from michael@0: // UTF-32. michael@0: int out_count = out[1] ? 2 : 1; michael@0: size_t out_size = sizeof(uint16_t) * out_count; michael@0: result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); michael@0: out_idx += out_count; michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: bool MinidumpFileWriter::CopyStringToMDString(const char *str, michael@0: unsigned int length, michael@0: TypedMDRVA *mdstring) { michael@0: bool result = true; michael@0: uint16_t out[2]; michael@0: int out_idx = 0; michael@0: michael@0: // Copy the string character by character michael@0: while (length && result) { michael@0: int conversion_count = UTF8ToUTF16Char(str, length, out); michael@0: if (!conversion_count) michael@0: return false; michael@0: michael@0: // Move the pointer along based on the nubmer of converted characters michael@0: length -= conversion_count; michael@0: str += conversion_count; michael@0: michael@0: // Append the one or two UTF-16 characters michael@0: int out_count = out[1] ? 2 : 1; michael@0: size_t out_size = sizeof(uint16_t) * out_count; michael@0: result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); michael@0: out_idx += out_count; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: template michael@0: bool MinidumpFileWriter::WriteStringCore(const CharType *str, michael@0: unsigned int length, michael@0: MDLocationDescriptor *location) { michael@0: assert(str); michael@0: assert(location); michael@0: // Calculate the mdstring length by either limiting to |length| as passed in michael@0: // or by finding the location of the NULL character. michael@0: unsigned int mdstring_length = 0; michael@0: if (!length) michael@0: length = INT_MAX; michael@0: for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length) michael@0: ; michael@0: michael@0: // Allocate the string buffer michael@0: TypedMDRVA mdstring(this); michael@0: if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(uint16_t))) michael@0: return false; michael@0: michael@0: // Set length excluding the NULL and copy the string michael@0: mdstring.get()->length = michael@0: static_cast(mdstring_length * sizeof(uint16_t)); michael@0: bool result = CopyStringToMDString(str, mdstring_length, &mdstring); michael@0: michael@0: // NULL terminate michael@0: if (result) { michael@0: uint16_t ch = 0; michael@0: result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch)); michael@0: michael@0: if (result) michael@0: *location = mdstring.location(); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length, michael@0: MDLocationDescriptor *location) { michael@0: return WriteStringCore(str, length, location); michael@0: } michael@0: michael@0: bool MinidumpFileWriter::WriteString(const char *str, unsigned int length, michael@0: MDLocationDescriptor *location) { michael@0: return WriteStringCore(str, length, location); michael@0: } michael@0: michael@0: bool MinidumpFileWriter::WriteMemory(const void *src, size_t size, michael@0: MDMemoryDescriptor *output) { michael@0: assert(src); michael@0: assert(output); michael@0: UntypedMDRVA mem(this); michael@0: michael@0: if (!mem.Allocate(size)) michael@0: return false; michael@0: if (!mem.Copy(src, mem.size())) michael@0: return false; michael@0: michael@0: output->start_of_memory_range = reinterpret_cast(src); michael@0: output->memory = mem.location(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: MDRVA MinidumpFileWriter::Allocate(size_t size) { michael@0: assert(size); michael@0: assert(file_ != -1); michael@0: size_t aligned_size = (size + 7) & ~7; // 64-bit alignment michael@0: michael@0: if (position_ + aligned_size > size_) { michael@0: size_t growth = aligned_size; michael@0: size_t minimal_growth = getpagesize(); michael@0: michael@0: // Ensure that the file grows by at least the size of a memory page michael@0: if (growth < minimal_growth) michael@0: growth = minimal_growth; michael@0: michael@0: size_t new_size = size_ + growth; michael@0: if (ftruncate(file_, new_size) != 0) michael@0: return kInvalidMDRVA; michael@0: michael@0: size_ = new_size; michael@0: } michael@0: michael@0: MDRVA current_position = position_; michael@0: position_ += static_cast(aligned_size); michael@0: michael@0: return current_position; michael@0: } michael@0: michael@0: bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) { michael@0: assert(src); michael@0: assert(size); michael@0: assert(file_ != -1); michael@0: michael@0: // Ensure that the data will fit in the allocated space michael@0: if (static_cast(size + position) > size_) michael@0: return false; michael@0: michael@0: // Seek and write the data michael@0: #if __linux__ michael@0: if (sys_lseek(file_, position, SEEK_SET) == static_cast(position)) { michael@0: if (sys_write(file_, src, size) == size) { michael@0: #else michael@0: if (lseek(file_, position, SEEK_SET) == static_cast(position)) { michael@0: if (write(file_, src, size) == size) { michael@0: #endif michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool UntypedMDRVA::Allocate(size_t size) { michael@0: assert(size_ == 0); michael@0: size_ = size; michael@0: position_ = writer_->Allocate(size_); michael@0: return position_ != MinidumpFileWriter::kInvalidMDRVA; michael@0: } michael@0: michael@0: bool UntypedMDRVA::Copy(MDRVA pos, const void *src, size_t size) { michael@0: assert(src); michael@0: assert(size); michael@0: assert(pos + size <= position_ + size_); michael@0: return writer_->Copy(pos, src, size); michael@0: } michael@0: michael@0: } // namespace google_breakpad