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