• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CheckNeedsFTruncateWorkAround(int file)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 
NeedsFTruncateWorkAround()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 
MinidumpFileWriter()92 MinidumpFileWriter::MinidumpFileWriter()
93     : file_(-1),
94       close_file_when_destroyed_(true),
95       position_(0),
96       size_(0) {
97 }
98 
~MinidumpFileWriter()99 MinidumpFileWriter::~MinidumpFileWriter() {
100   if (close_file_when_destroyed_)
101     Close();
102 }
103 
Open(const char * path)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 
SetFile(const int file)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 
Close()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 
CopyStringToMDString(const wchar_t * str,unsigned int length,TypedMDRVA<MDString> * mdstring)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 
CopyStringToMDString(const char * str,unsigned int length,TypedMDRVA<MDString> * mdstring)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>
WriteStringCore(const CharType * str,unsigned int length,MDLocationDescriptor * location)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 
WriteString(const wchar_t * str,unsigned int length,MDLocationDescriptor * location)243 bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length,
244                  MDLocationDescriptor *location) {
245   return WriteStringCore(str, length, location);
246 }
247 
WriteString(const char * str,unsigned int length,MDLocationDescriptor * location)248 bool MinidumpFileWriter::WriteString(const char *str, unsigned int length,
249                  MDLocationDescriptor *location) {
250   return WriteStringCore(str, length, location);
251 }
252 
WriteMemory(const void * src,size_t size,MDMemoryDescriptor * output)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 
Allocate(size_t size)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 
Copy(MDRVA position,const void * src,ssize_t size)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 
Allocate(size_t size)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 
Copy(MDRVA pos,const void * src,size_t size)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