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 "client/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 | MinidumpFileWriter::MinidumpFileWriter() |
93 | : file_(-1), |
94 | close_file_when_destroyed_(true), |
95 | position_(0), |
96 | size_(0) { |
97 | } |
98 | |
99 | MinidumpFileWriter::~MinidumpFileWriter() { |
100 | if (close_file_when_destroyed_) |
101 | Close(); |
102 | } |
103 | |
104 | bool MinidumpFileWriter::Open(const char* path) { |
105 | assert(file_ == -1); |
106 | #if defined(__linux__) && __linux__ |
107 | 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 | return file_ != -1; |
113 | } |
114 | |
115 | void MinidumpFileWriter::SetFile(const int file) { |
116 | assert(file_ == -1); |
117 | file_ = file; |
118 | close_file_when_destroyed_ = false; |
119 | #if defined(__ANDROID__) |
120 | CheckNeedsFTruncateWorkAround(file); |
121 | #endif |
122 | } |
123 | |
124 | bool MinidumpFileWriter::Close() { |
125 | bool result = true; |
126 | |
127 | if (file_ != -1) { |
128 | #if defined(__ANDROID__) |
129 | if (!NeedsFTruncateWorkAround() && ftruncate(file_, position_)) { |
130 | return false; |
131 | } |
132 | #else |
133 | if (ftruncate(file_, position_)) { |
134 | return false; |
135 | } |
136 | #endif |
137 | #if defined(__linux__) && __linux__ |
138 | result = (sys_close(file_) == 0); |
139 | #else |
140 | result = (close(file_) == 0); |
141 | #endif |
142 | file_ = -1; |
143 | } |
144 | |
145 | return result; |
146 | } |
147 | |
148 | bool MinidumpFileWriter::CopyStringToMDString(const wchar_t* str, |
149 | unsigned int length, |
150 | TypedMDRVA<MDString>* mdstring) { |
151 | 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 | int out_idx = 0; |
158 | |
159 | // Copy the string character by character |
160 | while (length && result) { |
161 | UTF32ToUTF16Char(*str, out); |
162 | if (!out[0]) |
163 | return false; |
164 | |
165 | // Process one character at a time |
166 | --length; |
167 | ++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 | int out_count = out[1] ? 2 : 1; |
173 | size_t out_size = sizeof(uint16_t) * out_count; |
174 | result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); |
175 | out_idx += out_count; |
176 | } |
177 | } |
178 | return result; |
179 | } |
180 | |
181 | bool MinidumpFileWriter::CopyStringToMDString(const char* str, |
182 | unsigned int length, |
183 | TypedMDRVA<MDString>* mdstring) { |
184 | bool result = true; |
185 | uint16_t out[2]; |
186 | int out_idx = 0; |
187 | |
188 | // Copy the string character by character |
189 | while (length && result) { |
190 | int conversion_count = UTF8ToUTF16Char(str, length, out); |
191 | if (!conversion_count) |
192 | return false; |
193 | |
194 | // Move the pointer along based on the nubmer of converted characters |
195 | length -= conversion_count; |
196 | str += conversion_count; |
197 | |
198 | // Append the one or two UTF-16 characters |
199 | int out_count = out[1] ? 2 : 1; |
200 | size_t out_size = sizeof(uint16_t) * out_count; |
201 | result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); |
202 | out_idx += out_count; |
203 | } |
204 | return result; |
205 | } |
206 | |
207 | template <typename CharType> |
208 | bool MinidumpFileWriter::WriteStringCore(const CharType* str, |
209 | unsigned int length, |
210 | MDLocationDescriptor* location) { |
211 | assert(str); |
212 | 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 | unsigned int mdstring_length = 0; |
216 | if (!length) |
217 | length = INT_MAX; |
218 | for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length) |
219 | ; |
220 | |
221 | // Allocate the string buffer |
222 | TypedMDRVA<MDString> mdstring(this); |
223 | if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(uint16_t))) |
224 | return false; |
225 | |
226 | // Set length excluding the NULL and copy the string |
227 | mdstring.get()->length = |
228 | static_cast<uint32_t>(mdstring_length * sizeof(uint16_t)); |
229 | bool result = CopyStringToMDString(str, mdstring_length, &mdstring); |
230 | |
231 | // NULL terminate |
232 | if (result) { |
233 | uint16_t ch = 0; |
234 | result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch)); |
235 | |
236 | if (result) |
237 | *location = mdstring.location(); |
238 | } |
239 | |
240 | return result; |
241 | } |
242 | |
243 | bool MinidumpFileWriter::WriteString(const wchar_t* str, unsigned int length, |
244 | MDLocationDescriptor* location) { |
245 | return WriteStringCore(str, length, location); |
246 | } |
247 | |
248 | bool MinidumpFileWriter::WriteString(const char* str, unsigned int length, |
249 | MDLocationDescriptor* location) { |
250 | return WriteStringCore(str, length, location); |
251 | } |
252 | |
253 | bool MinidumpFileWriter::WriteMemory(const void* src, size_t size, |
254 | MDMemoryDescriptor* output) { |
255 | assert(src); |
256 | assert(output); |
257 | UntypedMDRVA mem(this); |
258 | |
259 | if (!mem.Allocate(size)) |
260 | return false; |
261 | if (!mem.Copy(src, mem.size())) |
262 | return false; |
263 | |
264 | output->start_of_memory_range = reinterpret_cast<uint64_t>(src); |
265 | output->memory = mem.location(); |
266 | |
267 | return true; |
268 | } |
269 | |
270 | MDRVA MinidumpFileWriter::Allocate(size_t size) { |
271 | assert(size); |
272 | 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 | size_t aligned_size = (size + 7) & ~7; // 64-bit alignment |
288 | |
289 | if (position_ + aligned_size > size_) { |
290 | size_t growth = aligned_size; |
291 | size_t minimal_growth = getpagesize(); |
292 | |
293 | // Ensure that the file grows by at least the size of a memory page |
294 | if (growth < minimal_growth) |
295 | growth = minimal_growth; |
296 | |
297 | size_t new_size = size_ + growth; |
298 | if (ftruncate(file_, new_size) != 0) |
299 | return kInvalidMDRVA; |
300 | |
301 | size_ = new_size; |
302 | } |
303 | |
304 | MDRVA current_position = position_; |
305 | position_ += static_cast<MDRVA>(aligned_size); |
306 | |
307 | return current_position; |
308 | } |
309 | |
310 | bool MinidumpFileWriter::Copy(MDRVA position, const void* src, ssize_t size) { |
311 | assert(src); |
312 | assert(size); |
313 | assert(file_ != -1); |
314 | |
315 | // Ensure that the data will fit in the allocated space |
316 | if (static_cast<size_t>(size + position) > size_) |
317 | return false; |
318 | |
319 | // Seek and write the data |
320 | #if defined(__linux__) && __linux__ |
321 | if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) { |
322 | if (sys_write(file_, src, size) == size) { |
323 | 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 | return false; |
334 | } |
335 | |
336 | bool UntypedMDRVA::Allocate(size_t size) { |
337 | assert(size_ == 0); |
338 | size_ = size; |
339 | position_ = writer_->Allocate(size_); |
340 | return position_ != MinidumpFileWriter::kInvalidMDRVA; |
341 | } |
342 | |
343 | bool UntypedMDRVA::Copy(MDRVA pos, const void* src, size_t size) { |
344 | assert(src); |
345 | assert(size); |
346 | assert(pos + size <= position_ + size_); |
347 | return writer_->Copy(pos, src, size); |
348 | } |
349 | |
350 | } // namespace google_breakpad |
351 | |