Line data Source code
1 : // Copyright (c) 2006, Google Inc.
2 : // All rights reserved.
3 : //
4 : // Redistribution and use in source and binary forms, with or without
5 : // modification, are permitted provided that the following conditions are
6 : // met:
7 : //
8 : // * Redistributions of source code must retain the above copyright
9 : // notice, this list of conditions and the following disclaimer.
10 : // * Redistributions in binary form must reproduce the above
11 : // copyright notice, this list of conditions and the following disclaimer
12 : // in the documentation and/or other materials provided with the
13 : // distribution.
14 : // * Neither the name of Google Inc. nor the names of its
15 : // contributors may be used to endorse or promote products derived from
16 : // this software without specific prior written permission.
17 : //
18 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 :
30 : // minidump_file_writer.cc: Minidump file writer implementation.
31 : //
32 : // See minidump_file_writer.h for documentation.
33 :
34 : #include <fcntl.h>
35 : #include <limits.h>
36 : #include <stdio.h>
37 : #include <string.h>
38 : #include <unistd.h>
39 :
40 : #include "minidump_file_writer-inl.h"
41 : #include "common/linux/linux_libc_support.h"
42 : #include "common/string_conversion.h"
43 : #if defined(__linux__) && __linux__
44 : #include "third_party/lss/linux_syscall_support.h"
45 : #endif
46 :
47 : #if defined(__ANDROID__)
48 : #include <errno.h>
49 :
50 : namespace {
51 :
52 : bool g_need_ftruncate_workaround = false;
53 : bool g_checked_need_ftruncate_workaround = false;
54 :
55 : void CheckNeedsFTruncateWorkAround(int file) {
56 : if (g_checked_need_ftruncate_workaround) {
57 : return;
58 : }
59 : g_checked_need_ftruncate_workaround = true;
60 :
61 : // Attempt an idempotent truncate that chops off nothing and see if we
62 : // run into any sort of errors.
63 : off_t offset = sys_lseek(file, 0, SEEK_END);
64 : if (offset == -1) {
65 : // lseek failed. Don't apply work around. It's unlikely that we can write
66 : // to a minidump with either method.
67 : return;
68 : }
69 :
70 : int result = ftruncate(file, offset);
71 : if (result == -1 && errno == EACCES) {
72 : // It very likely that we are running into the kernel bug in M devices.
73 : // We are going to deploy the workaround for writing minidump files
74 : // without uses of ftruncate(). This workaround should be fine even
75 : // for kernels without the bug.
76 : // See http://crbug.com/542840 for more details.
77 : g_need_ftruncate_workaround = true;
78 : }
79 : }
80 :
81 : bool NeedsFTruncateWorkAround() {
82 : return g_need_ftruncate_workaround;
83 : }
84 :
85 : } // namespace
86 : #endif // defined(__ANDROID__)
87 :
88 : namespace google_breakpad {
89 :
90 : const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
91 :
92 0 : MinidumpFileWriter::MinidumpFileWriter()
93 : : file_(-1),
94 : close_file_when_destroyed_(true),
95 : position_(0),
96 0 : size_(0) {
97 0 : }
98 :
99 0 : MinidumpFileWriter::~MinidumpFileWriter() {
100 0 : if (close_file_when_destroyed_)
101 0 : Close();
102 0 : }
103 :
104 0 : bool MinidumpFileWriter::Open(const char *path) {
105 0 : assert(file_ == -1);
106 : #if defined(__linux__) && __linux__
107 0 : file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
108 : #else
109 : file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
110 : #endif
111 :
112 0 : return file_ != -1;
113 : }
114 :
115 0 : void MinidumpFileWriter::SetFile(const int file) {
116 0 : assert(file_ == -1);
117 0 : file_ = file;
118 0 : close_file_when_destroyed_ = false;
119 : #if defined(__ANDROID__)
120 : CheckNeedsFTruncateWorkAround(file);
121 : #endif
122 0 : }
123 :
124 0 : bool MinidumpFileWriter::Close() {
125 0 : bool result = true;
126 :
127 0 : if (file_ != -1) {
128 : #if defined(__ANDROID__)
129 : if (!NeedsFTruncateWorkAround() && ftruncate(file_, position_)) {
130 : return false;
131 : }
132 : #else
133 0 : if (ftruncate(file_, position_)) {
134 0 : return false;
135 : }
136 : #endif
137 : #if defined(__linux__) && __linux__
138 0 : result = (sys_close(file_) == 0);
139 : #else
140 : result = (close(file_) == 0);
141 : #endif
142 0 : file_ = -1;
143 : }
144 :
145 0 : return result;
146 : }
147 :
148 0 : bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str,
149 : unsigned int length,
150 : TypedMDRVA<MDString> *mdstring) {
151 0 : bool result = true;
152 : if (sizeof(wchar_t) == sizeof(uint16_t)) {
153 : // Shortcut if wchar_t is the same size as MDString's buffer
154 : result = mdstring->Copy(str, mdstring->get()->length);
155 : } else {
156 : uint16_t out[2];
157 0 : int out_idx = 0;
158 :
159 : // Copy the string character by character
160 0 : while (length && result) {
161 0 : UTF32ToUTF16Char(*str, out);
162 0 : if (!out[0])
163 0 : return false;
164 :
165 : // Process one character at a time
166 0 : --length;
167 0 : ++str;
168 :
169 : // Append the one or two UTF-16 characters. The first one will be non-
170 : // zero, but the second one may be zero, depending on the conversion from
171 : // UTF-32.
172 0 : int out_count = out[1] ? 2 : 1;
173 0 : size_t out_size = sizeof(uint16_t) * out_count;
174 0 : result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
175 0 : out_idx += out_count;
176 : }
177 : }
178 0 : return result;
179 : }
180 :
181 0 : bool MinidumpFileWriter::CopyStringToMDString(const char *str,
182 : unsigned int length,
183 : TypedMDRVA<MDString> *mdstring) {
184 0 : bool result = true;
185 : uint16_t out[2];
186 0 : int out_idx = 0;
187 :
188 : // Copy the string character by character
189 0 : while (length && result) {
190 0 : int conversion_count = UTF8ToUTF16Char(str, length, out);
191 0 : if (!conversion_count)
192 0 : return false;
193 :
194 : // Move the pointer along based on the nubmer of converted characters
195 0 : length -= conversion_count;
196 0 : str += conversion_count;
197 :
198 : // Append the one or two UTF-16 characters
199 0 : int out_count = out[1] ? 2 : 1;
200 0 : size_t out_size = sizeof(uint16_t) * out_count;
201 0 : result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
202 0 : out_idx += out_count;
203 : }
204 0 : return result;
205 : }
206 :
207 : template <typename CharType>
208 0 : bool MinidumpFileWriter::WriteStringCore(const CharType *str,
209 : unsigned int length,
210 : MDLocationDescriptor *location) {
211 0 : assert(str);
212 0 : assert(location);
213 : // Calculate the mdstring length by either limiting to |length| as passed in
214 : // or by finding the location of the NULL character.
215 0 : unsigned int mdstring_length = 0;
216 0 : if (!length)
217 0 : length = INT_MAX;
218 0 : for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length)
219 : ;
220 :
221 : // Allocate the string buffer
222 0 : TypedMDRVA<MDString> mdstring(this);
223 0 : if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(uint16_t)))
224 0 : return false;
225 :
226 : // Set length excluding the NULL and copy the string
227 0 : mdstring.get()->length =
228 0 : static_cast<uint32_t>(mdstring_length * sizeof(uint16_t));
229 0 : bool result = CopyStringToMDString(str, mdstring_length, &mdstring);
230 :
231 : // NULL terminate
232 0 : if (result) {
233 0 : uint16_t ch = 0;
234 0 : result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch));
235 :
236 0 : if (result)
237 0 : *location = mdstring.location();
238 : }
239 :
240 0 : return result;
241 : }
242 :
243 0 : bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length,
244 : MDLocationDescriptor *location) {
245 0 : return WriteStringCore(str, length, location);
246 : }
247 :
248 0 : bool MinidumpFileWriter::WriteString(const char *str, unsigned int length,
249 : MDLocationDescriptor *location) {
250 0 : return WriteStringCore(str, length, location);
251 : }
252 :
253 0 : bool MinidumpFileWriter::WriteMemory(const void *src, size_t size,
254 : MDMemoryDescriptor *output) {
255 0 : assert(src);
256 0 : assert(output);
257 0 : UntypedMDRVA mem(this);
258 :
259 0 : if (!mem.Allocate(size))
260 0 : return false;
261 0 : if (!mem.Copy(src, mem.size()))
262 0 : return false;
263 :
264 0 : output->start_of_memory_range = reinterpret_cast<uint64_t>(src);
265 0 : output->memory = mem.location();
266 :
267 0 : return true;
268 : }
269 :
270 0 : MDRVA MinidumpFileWriter::Allocate(size_t size) {
271 0 : assert(size);
272 0 : assert(file_ != -1);
273 : #if defined(__ANDROID__)
274 : if (NeedsFTruncateWorkAround()) {
275 : // If ftruncate() is not available. We simply increase the size beyond the
276 : // current file size. sys_write() will expand the file when data is written
277 : // to it. Because we did not over allocate to fit memory pages, we also
278 : // do not need to ftruncate() the file once we are done.
279 : size_ += size;
280 :
281 : // We don't need to seek since the file is unchanged.
282 : MDRVA current_position = position_;
283 : position_ += static_cast<MDRVA>(size);
284 : return current_position;
285 : }
286 : #endif
287 0 : size_t aligned_size = (size + 7) & ~7; // 64-bit alignment
288 :
289 0 : if (position_ + aligned_size > size_) {
290 0 : size_t growth = aligned_size;
291 0 : size_t minimal_growth = getpagesize();
292 :
293 : // Ensure that the file grows by at least the size of a memory page
294 0 : if (growth < minimal_growth)
295 0 : growth = minimal_growth;
296 :
297 0 : size_t new_size = size_ + growth;
298 0 : if (ftruncate(file_, new_size) != 0)
299 0 : return kInvalidMDRVA;
300 :
301 0 : size_ = new_size;
302 : }
303 :
304 0 : MDRVA current_position = position_;
305 0 : position_ += static_cast<MDRVA>(aligned_size);
306 :
307 0 : return current_position;
308 : }
309 :
310 0 : bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) {
311 0 : assert(src);
312 0 : assert(size);
313 0 : assert(file_ != -1);
314 :
315 : // Ensure that the data will fit in the allocated space
316 0 : if (static_cast<size_t>(size + position) > size_)
317 0 : return false;
318 :
319 : // Seek and write the data
320 : #if defined(__linux__) && __linux__
321 0 : if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
322 0 : if (sys_write(file_, src, size) == size) {
323 0 : return true;
324 : }
325 : }
326 : #else
327 : if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
328 : if (write(file_, src, size) == size) {
329 : return true;
330 : }
331 : }
332 : #endif
333 0 : return false;
334 : }
335 :
336 0 : bool UntypedMDRVA::Allocate(size_t size) {
337 0 : assert(size_ == 0);
338 0 : size_ = size;
339 0 : position_ = writer_->Allocate(size_);
340 0 : return position_ != MinidumpFileWriter::kInvalidMDRVA;
341 : }
342 :
343 0 : bool UntypedMDRVA::Copy(MDRVA pos, const void *src, size_t size) {
344 0 : assert(src);
345 0 : assert(size);
346 0 : assert(pos + size <= position_ + size_);
347 0 : return writer_->Copy(pos, src, size);
348 : }
349 :
350 : } // namespace google_breakpad
|