• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011, 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 // crash_generator.cc: Implement google_breakpad::CrashGenerator.
31 // See crash_generator.h for details.
32 
33 #include "common/linux/tests/crash_generator.h"
34 
35 #include <pthread.h>
36 #include <signal.h>
37 #include <stdio.h>
38 #include <sys/mman.h>
39 #include <sys/resource.h>
40 #include <sys/syscall.h>
41 #include <sys/wait.h>
42 #include <unistd.h>
43 
44 #include <string>
45 
46 #if defined(__ANDROID__)
47 #include "common/android/testing/pthread_fixes.h"
48 #endif
49 #include "common/linux/eintr_wrapper.h"
50 #include "common/tests/auto_tempdir.h"
51 #include "common/tests/file_utils.h"
52 #include "common/using_std_string.h"
53 
54 namespace {
55 
56 struct ThreadData {
57   pthread_t thread;
58   pthread_barrier_t* barrier;
59   pid_t* thread_id_ptr;
60 };
61 
62 const char* const kProcFilesToCopy[] = {
63   "auxv", "cmdline", "environ", "maps", "status"
64 };
65 const size_t kNumProcFilesToCopy =
66     sizeof(kProcFilesToCopy) / sizeof(kProcFilesToCopy[0]);
67 
gettid()68 int gettid() {
69   // Glibc does not provide a wrapper for this.
70   return syscall(__NR_gettid);
71 }
72 
tkill(pid_t tid,int sig)73 int tkill(pid_t tid, int sig) {
74   // Glibc does not provide a wrapper for this.
75   return syscall(__NR_tkill, tid, sig);
76 }
77 
78 // Core file size limit set to 1 MB, which is big enough for test purposes.
79 const rlim_t kCoreSizeLimit = 1024 * 1024;
80 
thread_function(void * data)81 void *thread_function(void *data) {
82   ThreadData* thread_data = reinterpret_cast<ThreadData*>(data);
83   volatile pid_t thread_id = gettid();
84   *(thread_data->thread_id_ptr) = thread_id;
85   int result = pthread_barrier_wait(thread_data->barrier);
86   if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) {
87     perror("Failed to wait for sync barrier");
88     exit(1);
89   }
90   while (true) {
91     pthread_yield();
92   }
93 }
94 
95 }  // namespace
96 
97 namespace google_breakpad {
98 
CrashGenerator()99 CrashGenerator::CrashGenerator()
100     : shared_memory_(NULL),
101       shared_memory_size_(0) {
102 }
103 
~CrashGenerator()104 CrashGenerator::~CrashGenerator() {
105   UnmapSharedMemory();
106 }
107 
HasDefaultCorePattern() const108 bool CrashGenerator::HasDefaultCorePattern() const {
109   char buffer[8];
110   ssize_t buffer_size = sizeof(buffer);
111   return ReadFile("/proc/sys/kernel/core_pattern", buffer, &buffer_size) &&
112          buffer_size == 5 && memcmp(buffer, "core", 4) == 0;
113 }
114 
GetCoreFilePath() const115 string CrashGenerator::GetCoreFilePath() const {
116   return temp_dir_.path() + "/core";
117 }
118 
GetDirectoryOfProcFilesCopy() const119 string CrashGenerator::GetDirectoryOfProcFilesCopy() const {
120   return temp_dir_.path() + "/proc";
121 }
122 
GetThreadId(unsigned index) const123 pid_t CrashGenerator::GetThreadId(unsigned index) const {
124   return reinterpret_cast<pid_t*>(shared_memory_)[index];
125 }
126 
GetThreadIdPointer(unsigned index)127 pid_t* CrashGenerator::GetThreadIdPointer(unsigned index) {
128   return reinterpret_cast<pid_t*>(shared_memory_) + index;
129 }
130 
MapSharedMemory(size_t memory_size)131 bool CrashGenerator::MapSharedMemory(size_t memory_size) {
132   if (!UnmapSharedMemory())
133     return false;
134 
135   void* mapped_memory = mmap(0, memory_size, PROT_READ | PROT_WRITE,
136                              MAP_SHARED | MAP_ANONYMOUS, -1, 0);
137   if (mapped_memory == MAP_FAILED) {
138     perror("CrashGenerator: Failed to map shared memory");
139     return false;
140   }
141 
142   memset(mapped_memory, 0, memory_size);
143   shared_memory_ = mapped_memory;
144   shared_memory_size_ = memory_size;
145   return true;
146 }
147 
UnmapSharedMemory()148 bool CrashGenerator::UnmapSharedMemory() {
149   if (!shared_memory_)
150     return true;
151 
152   if (munmap(shared_memory_, shared_memory_size_) == 0) {
153     shared_memory_ = NULL;
154     shared_memory_size_ = 0;
155     return true;
156   }
157 
158   perror("CrashGenerator: Failed to unmap shared memory");
159   return false;
160 }
161 
SetCoreFileSizeLimit(rlim_t limit) const162 bool CrashGenerator::SetCoreFileSizeLimit(rlim_t limit) const {
163   struct rlimit limits = { limit, limit };
164   if (setrlimit(RLIMIT_CORE, &limits) == -1) {
165     perror("CrashGenerator: Failed to set core file size limit");
166     return false;
167   }
168   return true;
169 }
170 
CreateChildCrash(unsigned num_threads,unsigned crash_thread,int crash_signal,pid_t * child_pid)171 bool CrashGenerator::CreateChildCrash(
172     unsigned num_threads, unsigned crash_thread, int crash_signal,
173     pid_t* child_pid) {
174   if (num_threads == 0 || crash_thread >= num_threads) {
175     fprintf(stderr, "CrashGenerator: Invalid thread counts; num_threads=%u"
176                     " crash_thread=%u\n", num_threads, crash_thread);
177     return false;
178   }
179 
180   if (!MapSharedMemory(num_threads * sizeof(pid_t))) {
181     perror("CrashGenerator: Unable to map shared memory");
182     return false;
183   }
184 
185   pid_t pid = fork();
186   if (pid == 0) {
187     if (chdir(temp_dir_.path().c_str()) == -1) {
188       perror("CrashGenerator: Failed to change directory");
189       exit(1);
190     }
191     if (SetCoreFileSizeLimit(kCoreSizeLimit)) {
192       CreateThreadsInChildProcess(num_threads);
193       string proc_dir = GetDirectoryOfProcFilesCopy();
194       if (mkdir(proc_dir.c_str(), 0755) == -1) {
195         perror("CrashGenerator: Failed to create proc directory");
196         exit(1);
197       }
198       if (!CopyProcFiles(getpid(), proc_dir.c_str())) {
199         fprintf(stderr, "CrashGenerator: Failed to copy proc files\n");
200         exit(1);
201       }
202       // On Android the signal sometimes doesn't seem to get sent even though
203       // tkill returns '0'.  Retry a couple of times if the signal doesn't get
204       // through on the first go:
205       // https://code.google.com/p/google-breakpad/issues/detail?id=579
206 #if defined(__ANDROID__)
207       const int kRetries = 60;
208       const unsigned int kSleepTimeInSeconds = 1;
209 #else
210       const int kRetries = 1;
211       const unsigned int kSleepTimeInSeconds = 600;
212 #endif
213       for (int i = 0; i < kRetries; i++) {
214         if (tkill(*GetThreadIdPointer(crash_thread), crash_signal) == -1) {
215           perror("CrashGenerator: Failed to kill thread by signal");
216         } else {
217           // At this point, we've queued the signal for delivery, but there's no
218           // guarantee when it'll be delivered.  We don't want the main thread to
219           // race and exit before the thread we signaled is processed.  So sleep
220           // long enough that we won't flake even under fairly high load.
221           // TODO: See if we can't be a bit more deterministic.  There doesn't
222           // seem to be an API to check on signal delivery status, so we can't
223           // really poll and wait for the kernel to declare the signal has been
224           // delivered.  If it has, and things worked, we'd be killed, so the
225           // sleep length doesn't really matter.
226           sleep(kSleepTimeInSeconds);
227         }
228       }
229     } else {
230       perror("CrashGenerator: Failed to set core limit");
231     }
232     exit(1);
233   } else if (pid == -1) {
234     perror("CrashGenerator: Failed to create child process");
235     return false;
236   }
237 
238   int status;
239   if (HANDLE_EINTR(waitpid(pid, &status, 0)) == -1) {
240     perror("CrashGenerator: Failed to wait for child process");
241     return false;
242   }
243   if (!WIFSIGNALED(status) || WTERMSIG(status) != crash_signal) {
244     fprintf(stderr, "CrashGenerator: Child process not killed by the expected signal\n"
245                     "  exit status=0x%x pid=%u signaled=%s sig=%d expected=%d\n",
246                     status, pid, WIFSIGNALED(status) ? "true" : "false",
247                     WTERMSIG(status), crash_signal);
248     return false;
249   }
250 
251   if (child_pid)
252     *child_pid = pid;
253   return true;
254 }
255 
CopyProcFiles(pid_t pid,const char * path) const256 bool CrashGenerator::CopyProcFiles(pid_t pid, const char* path) const {
257   char from_path[PATH_MAX], to_path[PATH_MAX];
258   for (size_t i = 0; i < kNumProcFilesToCopy; ++i) {
259     int num_chars = snprintf(from_path, PATH_MAX, "/proc/%d/%s",
260                              pid, kProcFilesToCopy[i]);
261     if (num_chars < 0 || num_chars >= PATH_MAX)
262       return false;
263 
264     num_chars = snprintf(to_path, PATH_MAX, "%s/%s",
265                          path, kProcFilesToCopy[i]);
266     if (num_chars < 0 || num_chars >= PATH_MAX)
267       return false;
268 
269     if (!CopyFile(from_path, to_path))
270       return false;
271   }
272   return true;
273 }
274 
CreateThreadsInChildProcess(unsigned num_threads)275 void CrashGenerator::CreateThreadsInChildProcess(unsigned num_threads) {
276   *GetThreadIdPointer(0) = getpid();
277 
278   if (num_threads <= 1)
279     return;
280 
281   // This method does not clean up any pthread resource, as the process
282   // is expected to be killed anyway.
283   ThreadData* thread_data = new ThreadData[num_threads];
284 
285   // Create detached threads so that we do not worry about pthread_join()
286   // later being called or not.
287   pthread_attr_t thread_attributes;
288   if (pthread_attr_init(&thread_attributes) != 0 ||
289       pthread_attr_setdetachstate(&thread_attributes,
290                                   PTHREAD_CREATE_DETACHED) != 0) {
291     fprintf(stderr, "CrashGenerator: Failed to initialize thread attribute\n");
292     exit(1);
293   }
294 
295   pthread_barrier_t thread_barrier;
296   if (pthread_barrier_init(&thread_barrier, NULL, num_threads) != 0) {
297     fprintf(stderr, "CrashGenerator: Failed to initialize thread barrier\n");
298     exit(1);
299   }
300 
301   for (unsigned i = 1; i < num_threads; ++i) {
302     thread_data[i].barrier = &thread_barrier;
303     thread_data[i].thread_id_ptr = GetThreadIdPointer(i);
304     if (pthread_create(&thread_data[i].thread, &thread_attributes,
305                        thread_function, &thread_data[i]) != 0) {
306       fprintf(stderr, "CrashGenerator: Failed to create thread %d\n", i);
307       exit(1);
308     }
309   }
310 
311   int result = pthread_barrier_wait(&thread_barrier);
312   if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) {
313     fprintf(stderr, "CrashGenerator: Failed to wait for thread barrier\n");
314     exit(1);
315   }
316 
317   pthread_barrier_destroy(&thread_barrier);
318   pthread_attr_destroy(&thread_attributes);
319   delete[] thread_data;
320 }
321 
322 }  // namespace google_breakpad
323