1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/crashreporter/google-breakpad/src/client/solaris/handler/solaris_lwp.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,436 @@ 1.4 +// Copyright (c) 2007, Google Inc. 1.5 +// All rights reserved. 1.6 +// 1.7 +// Redistribution and use in source and binary forms, with or without 1.8 +// modification, are permitted provided that the following conditions are 1.9 +// met: 1.10 +// 1.11 +// * Redistributions of source code must retain the above copyright 1.12 +// notice, this list of conditions and the following disclaimer. 1.13 +// * Redistributions in binary form must reproduce the above 1.14 +// copyright notice, this list of conditions and the following disclaimer 1.15 +// in the documentation and/or other materials provided with the 1.16 +// distribution. 1.17 +// * Neither the name of Google Inc. nor the names of its 1.18 +// contributors may be used to endorse or promote products derived from 1.19 +// this software without specific prior written permission. 1.20 +// 1.21 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.22 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.23 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.24 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.25 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.26 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.27 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.28 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.29 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.30 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.31 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.32 + 1.33 +// Author: Alfred Peng 1.34 + 1.35 +#include <dirent.h> 1.36 +#include <elf.h> 1.37 +#include <errno.h> 1.38 +#include <fcntl.h> 1.39 +#include <limits.h> 1.40 +#include <sys/frame.h> 1.41 +#include <sys/stat.h> 1.42 +#include <sys/types.h> 1.43 +#include <sys/wait.h> 1.44 +#include <unistd.h> 1.45 + 1.46 +#include <algorithm> 1.47 +#include <cassert> 1.48 +#include <cstdio> 1.49 +#include <cstdlib> 1.50 +#include <functional> 1.51 + 1.52 +#include "client/solaris/handler/solaris_lwp.h" 1.53 +#include "common/solaris/message_output.h" 1.54 + 1.55 +using namespace google_breakpad; 1.56 + 1.57 +// This unamed namespace contains helper function. 1.58 +namespace { 1.59 + 1.60 +uintptr_t stack_base_address = 0; 1.61 +static const int HEADER_MAX = 2000; 1.62 +static const int MAP_MAX = 1000; 1.63 + 1.64 +// Context information for the callbacks when validating address by listing 1.65 +// modules. 1.66 +struct AddressValidatingContext { 1.67 + uintptr_t address; 1.68 + bool is_mapped; 1.69 + 1.70 + AddressValidatingContext() : address(0UL), is_mapped(false) { 1.71 + } 1.72 +}; 1.73 + 1.74 +// Convert from string to int. 1.75 +static bool LocalAtoi(char *s, int *r) { 1.76 + assert(s != NULL); 1.77 + assert(r != NULL); 1.78 + char *endptr = NULL; 1.79 + int ret = strtol(s, &endptr, 10); 1.80 + if (endptr == s) 1.81 + return false; 1.82 + *r = ret; 1.83 + return true; 1.84 +} 1.85 + 1.86 +// Callback invoked for each mapped module. 1.87 +// It uses the module's adderss range to validate the address. 1.88 +static bool AddressNotInModuleCallback(const ModuleInfo &module_info, 1.89 + void *context) { 1.90 + AddressValidatingContext *addr = 1.91 + reinterpret_cast<AddressValidatingContext *>(context); 1.92 + if (addr->is_mapped = ((module_info.start_addr > 0) && 1.93 + (addr->address >= module_info.start_addr) && 1.94 + (addr->address <= module_info.start_addr + 1.95 + module_info.size))) { 1.96 + stack_base_address = module_info.start_addr + module_info.size; 1.97 + } 1.98 + 1.99 + return !addr->is_mapped; 1.100 +} 1.101 + 1.102 +static int IterateLwpAll(int pid, 1.103 + CallbackParam<LwpidCallback> *callback_param) { 1.104 + char lwp_path[40]; 1.105 + DIR *dir; 1.106 + int count = 0; 1.107 + 1.108 + snprintf(lwp_path, sizeof (lwp_path), "/proc/%d/lwp", (int)pid); 1.109 + if ((dir = opendir(lwp_path)) == NULL) 1.110 + return -1; 1.111 + 1.112 + struct dirent *entry = NULL; 1.113 + while ((entry = readdir(dir)) != NULL) { 1.114 + if ((strcmp(entry->d_name, ".") != 0) && 1.115 + (strcmp(entry->d_name, "..") != 0)) { 1.116 + int lwpid = 0; 1.117 + int last_pid = 0; 1.118 + if (LocalAtoi(entry->d_name, &lwpid) && last_pid != lwpid) { 1.119 + last_pid = lwpid; 1.120 + ++count; 1.121 + if (callback_param && 1.122 + !(callback_param->call_back)(lwpid, callback_param->context)) { 1.123 + break; 1.124 + } 1.125 + } 1.126 + } 1.127 + } 1.128 + 1.129 + closedir(dir); 1.130 + return count; 1.131 +} 1.132 + 1.133 +#if defined(__i386) && !defined(NO_FRAME_POINTER) 1.134 +void *GetNextFrame(void **last_ebp) { 1.135 + void *sp = *last_ebp; 1.136 + if ((unsigned long)sp == (unsigned long)last_ebp) 1.137 + return NULL; 1.138 + if ((unsigned long)sp & (sizeof(void *) - 1)) 1.139 + return NULL; 1.140 + if ((unsigned long)sp - (unsigned long)last_ebp > 100000) 1.141 + return NULL; 1.142 + return sp; 1.143 +} 1.144 +#elif defined(__sparc) 1.145 +void *GetNextFrame(void *last_ebp) { 1.146 + return reinterpret_cast<struct frame *>(last_ebp)->fr_savfp; 1.147 +} 1.148 +#else 1.149 +void *GetNextFrame(void **last_ebp) { 1.150 + return reinterpret_cast<void*>(last_ebp); 1.151 +} 1.152 +#endif 1.153 + 1.154 + 1.155 +class AutoCloser { 1.156 + public: 1.157 + AutoCloser(int fd) : fd_(fd) {} 1.158 + ~AutoCloser() { if (fd_) close(fd_); } 1.159 + private: 1.160 + int fd_; 1.161 +}; 1.162 + 1.163 +// Control the execution of the lwp. 1.164 +// Suspend/Resume lwp based on the value of context. 1.165 +static bool ControlLwp(int lwpid, void *context) { 1.166 + // The current thread is the one to handle the crash. Ignore it. 1.167 + if (lwpid != pthread_self()) { 1.168 + int ctlfd; 1.169 + char procname[PATH_MAX]; 1.170 + bool suspend = *(bool *)context; 1.171 + 1.172 + // Open the /proc/$pid/lwp/$lwpid/lwpctl files 1.173 + snprintf(procname, sizeof (procname), "/proc/self/lwp/%d/lwpctl", lwpid); 1.174 + 1.175 + if ((ctlfd = open(procname, O_WRONLY|O_EXCL)) < 0) { 1.176 + print_message2(2, "failed to open %s in ControlLwp\n", procname); 1.177 + return false; 1.178 + } 1.179 + 1.180 + AutoCloser autocloser(ctlfd); 1.181 + 1.182 + long ctl[2]; 1.183 + ctl[0] = suspend ? PCSTOP : PCRUN; 1.184 + ctl[1] = 0; 1.185 + if (write(ctlfd, ctl, sizeof (ctl)) != sizeof (ctl)) { 1.186 + print_message2(2, "failed in lwp %d\n", lwpid); 1.187 + return false; 1.188 + } 1.189 + } 1.190 + 1.191 + return true; 1.192 +} 1.193 + 1.194 +/* 1.195 + * Utility function to read the contents of a file that contains a 1.196 + * prheader_t at the start (/proc/$pid/lstatus or /proc/$pid/lpsinfo). 1.197 + * Return true on success. 1.198 + */ 1.199 +static bool read_lfile(int pid, const char *lname, prheader_t *lhp) { 1.200 + char lpath[PATH_MAX]; 1.201 + struct stat statb; 1.202 + int fd; 1.203 + size_t size; 1.204 + 1.205 + snprintf(lpath, sizeof (lpath), "/proc/%d/%s", pid, lname); 1.206 + if ((fd = open(lpath, O_RDONLY)) < 0) { 1.207 + print_message2(2, "failed to open %s in read_lfile\n", lpath); 1.208 + return false; 1.209 + } 1.210 + 1.211 + AutoCloser autocloser(fd); 1.212 + 1.213 + if (fstat(fd, &statb) != 0) 1.214 + return false; 1.215 + 1.216 + size = statb.st_size; 1.217 + if ((size / sizeof (prheader_t)) + 32 > HEADER_MAX) { 1.218 + print_message1(2, "map size overflow\n"); 1.219 + return false; 1.220 + } 1.221 + 1.222 + if (pread(fd, lhp, size, 0) <= sizeof (prheader_t)) 1.223 + return false; 1.224 + 1.225 + return true; 1.226 +} 1.227 + 1.228 +} // namespace 1.229 + 1.230 +namespace google_breakpad { 1.231 + 1.232 +SolarisLwp::SolarisLwp(int pid) : pid_(pid) { 1.233 +} 1.234 + 1.235 +SolarisLwp::~SolarisLwp() { 1.236 +} 1.237 + 1.238 +int SolarisLwp::ControlAllLwps(bool suspend) { 1.239 + CallbackParam<LwpidCallback> callback_param(ControlLwp, &suspend); 1.240 + return IterateLwpAll(pid_, &callback_param); 1.241 +} 1.242 + 1.243 +int SolarisLwp::GetLwpCount() const { 1.244 + return IterateLwpAll(pid_, NULL); 1.245 +} 1.246 + 1.247 +int SolarisLwp::Lwp_iter_all(int pid, 1.248 + CallbackParam<LwpCallback> *callback_param) const { 1.249 + lwpstatus_t *Lsp; 1.250 + lwpstatus_t *sp; 1.251 + prheader_t lphp[HEADER_MAX]; 1.252 + prheader_t lhp[HEADER_MAX]; 1.253 + prheader_t *Lphp = lphp; 1.254 + prheader_t *Lhp = lhp; 1.255 + lwpsinfo_t *Lpsp; 1.256 + long nstat; 1.257 + long ninfo; 1.258 + int rv = 0; 1.259 + 1.260 + /* 1.261 + * The /proc/pid/lstatus file has the array of lwpstatus_t's and the 1.262 + * /proc/pid/lpsinfo file has the array of lwpsinfo_t's. 1.263 + */ 1.264 + if (read_lfile(pid, "lstatus", Lhp) == NULL) 1.265 + return -1; 1.266 + if (read_lfile(pid, "lpsinfo", Lphp) == NULL) { 1.267 + return -1; 1.268 + } 1.269 + 1.270 + Lsp = (lwpstatus_t *)(uintptr_t)(Lhp + 1); 1.271 + Lpsp = (lwpsinfo_t *)(uintptr_t)(Lphp + 1); 1.272 + 1.273 + for (ninfo = Lphp->pr_nent; ninfo != 0; --ninfo) { 1.274 + if (Lpsp->pr_sname != 'Z') { 1.275 + sp = Lsp; 1.276 + Lsp = (lwpstatus_t *)((uintptr_t)Lsp + Lhp->pr_entsize); 1.277 + } else { 1.278 + sp = NULL; 1.279 + } 1.280 + if (callback_param && 1.281 + !(callback_param->call_back)(sp, callback_param->context)) 1.282 + break; 1.283 + ++rv; 1.284 + Lpsp = (lwpsinfo_t *)((uintptr_t)Lpsp + Lphp->pr_entsize); 1.285 + } 1.286 + 1.287 + return rv; 1.288 +} 1.289 + 1.290 +uintptr_t SolarisLwp::GetLwpStackBottom(uintptr_t current_esp) const { 1.291 + AddressValidatingContext addr; 1.292 + addr.address = current_esp; 1.293 + CallbackParam<ModuleCallback> callback_param(AddressNotInModuleCallback, 1.294 + &addr); 1.295 + ListModules(&callback_param); 1.296 + return stack_base_address; 1.297 +} 1.298 + 1.299 +int SolarisLwp::GetModuleCount() const { 1.300 + return ListModules(NULL); 1.301 +} 1.302 + 1.303 +int SolarisLwp::ListModules( 1.304 + CallbackParam<ModuleCallback> *callback_param) const { 1.305 + const char *maps_path = "/proc/self/map"; 1.306 + struct stat status; 1.307 + int fd = 0, num; 1.308 + prmap_t map_array[MAP_MAX]; 1.309 + prmap_t *maps = map_array; 1.310 + size_t size; 1.311 + 1.312 + if ((fd = open(maps_path, O_RDONLY)) == -1) { 1.313 + print_message2(2, "failed to open %s in ListModules\n", maps_path); 1.314 + return -1; 1.315 + } 1.316 + 1.317 + AutoCloser autocloser(fd); 1.318 + 1.319 + if (fstat(fd, &status)) 1.320 + return -1; 1.321 + 1.322 + /* 1.323 + * Determine number of mappings, this value must be 1.324 + * larger than the actual module count 1.325 + */ 1.326 + size = status.st_size; 1.327 + if ((num = (int)(size / sizeof (prmap_t))) > MAP_MAX) { 1.328 + print_message1(2, "map size overflow\n"); 1.329 + return -1; 1.330 + } 1.331 + 1.332 + if (read(fd, (void *)maps, size) < 0) { 1.333 + print_message2(2, "failed to read %d\n", fd); 1.334 + return -1; 1.335 + } 1.336 + 1.337 + prmap_t *_maps; 1.338 + int _num; 1.339 + int module_count = 0; 1.340 + 1.341 + /* 1.342 + * Scan each mapping - note it is assummed that the mappings are 1.343 + * presented in order. We fill holes between mappings. On intel 1.344 + * the last mapping is usually the data segment of ld.so.1, after 1.345 + * this comes a red zone into which non-fixed mapping won't get 1.346 + * place. Thus we can simply bail from the loop after seeing the 1.347 + * last mapping. 1.348 + */ 1.349 + for (_num = 0, _maps = maps; _num < num; ++_num, ++_maps) { 1.350 + ModuleInfo module; 1.351 + char *name = _maps->pr_mapname; 1.352 + 1.353 + memset(&module, 0, sizeof (module)); 1.354 + module.start_addr = _maps->pr_vaddr; 1.355 + module.size = _maps->pr_size; 1.356 + if (strlen(name) > 0) { 1.357 + int objectfd = 0; 1.358 + char path[PATH_MAX]; 1.359 + char buf[SELFMAG]; 1.360 + 1.361 + snprintf(path, sizeof (path), "/proc/self/object/%s", name); 1.362 + if ((objectfd = open(path, O_RDONLY)) < 0) { 1.363 + print_message1(2, "can't open module file\n"); 1.364 + continue; 1.365 + } 1.366 + 1.367 + AutoCloser autocloser(objectfd); 1.368 + 1.369 + if (read(objectfd, buf, SELFMAG) != SELFMAG) { 1.370 + print_message1(2, "can't read module file\n"); 1.371 + continue; 1.372 + } 1.373 + if (buf[0] != ELFMAG0 || buf[1] != ELFMAG1 || 1.374 + buf[2] != ELFMAG2 || buf[3] != ELFMAG3) { 1.375 + continue; 1.376 + } 1.377 + 1.378 + strncpy(module.name, name, sizeof (module.name) - 1); 1.379 + ++module_count; 1.380 + } 1.381 + if (callback_param && 1.382 + (!callback_param->call_back(module, callback_param->context))) { 1.383 + break; 1.384 + } 1.385 + } 1.386 + 1.387 + return module_count; 1.388 +} 1.389 + 1.390 +// Check if the address is a valid virtual address. 1.391 +// If the address is in any of the mapped modules, we take it as valid. 1.392 +// Otherwise it is invalid. 1.393 +bool SolarisLwp::IsAddressMapped(uintptr_t address) const { 1.394 + AddressValidatingContext addr; 1.395 + addr.address = address; 1.396 + CallbackParam<ModuleCallback> callback_param(AddressNotInModuleCallback, 1.397 + &addr); 1.398 + ListModules(&callback_param); 1.399 + return addr.is_mapped; 1.400 +} 1.401 + 1.402 +// We're looking for a ucontext_t as the second parameter 1.403 +// to a signal handler function call. Luckily, the ucontext_t 1.404 +// has an ebp(fp on SPARC) member which should match the ebp(fp) 1.405 +// pointed to by the ebp(fp) of the signal handler frame. 1.406 +// The Solaris stack looks like this: 1.407 +// http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libproc/common/Pstack.c#81 1.408 +bool SolarisLwp::FindSigContext(uintptr_t sighandler_ebp, 1.409 + ucontext_t **sig_ctx) { 1.410 + uintptr_t previous_ebp; 1.411 + uintptr_t sig_ebp; 1.412 + const int MAX_STACK_DEPTH = 50; 1.413 + int depth_counter = 0; 1.414 + 1.415 + do { 1.416 +#if TARGET_CPU_SPARC 1.417 + previous_ebp = reinterpret_cast<uintptr_t>(GetNextFrame( 1.418 + reinterpret_cast<void*>(sighandler_ebp))); 1.419 + *sig_ctx = reinterpret_cast<ucontext_t*>(sighandler_ebp + sizeof (struct frame)); 1.420 + uintptr_t sig_esp = (*sig_ctx)->uc_mcontext.gregs[REG_O6]; 1.421 + if (sig_esp < previous_ebp && sig_esp > sighandler_ebp) 1.422 + sig_ebp = (uintptr_t)(((struct frame *)sig_esp)->fr_savfp); 1.423 + 1.424 +#elif TARGET_CPU_X86 1.425 + previous_ebp = reinterpret_cast<uintptr_t>(GetNextFrame( 1.426 + reinterpret_cast<void**>(sighandler_ebp))); 1.427 + *sig_ctx = reinterpret_cast<ucontext_t*>(sighandler_ebp + sizeof (struct frame) + 1.428 + 3 * sizeof(uintptr_t)); 1.429 + sig_ebp = (*sig_ctx)->uc_mcontext.gregs[EBP]; 1.430 +#endif 1.431 + sighandler_ebp = previous_ebp; 1.432 + depth_counter++; 1.433 + } while(previous_ebp != sig_ebp && sighandler_ebp != 0 && 1.434 + IsAddressMapped(sighandler_ebp) && depth_counter < MAX_STACK_DEPTH); 1.435 + 1.436 + return previous_ebp == sig_ebp && previous_ebp != 0; 1.437 +} 1.438 + 1.439 +} // namespace google_breakpad