user_collector.cc revision 74dc62460b8cdd5bfeac47bfe8e759fc04b55ef8
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "user_collector.h" 18 19#include <elf.h> 20#include <fcntl.h> 21#include <grp.h> // For struct group. 22#include <pcrecpp.h> 23#include <pwd.h> // For struct passwd. 24#include <stdint.h> 25#include <sys/cdefs.h> // For __WORDSIZE 26#include <sys/fsuid.h> 27#include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS. 28#include <unistd.h> // For setgroups 29 30#include <iostream> // For std::oct 31#include <string> 32#include <vector> 33 34#include <base/files/file_util.h> 35#include <base/logging.h> 36#include <base/posix/eintr_wrapper.h> 37#include <base/strings/string_split.h> 38#include <base/strings/string_util.h> 39#include <base/strings/stringprintf.h> 40#include <brillo/osrelease_reader.h> 41#include <brillo/process.h> 42#include <brillo/syslog_logging.h> 43#include <cutils/properties.h> 44#include <private/android_filesystem_config.h> 45 46static const char kCollectionErrorSignature[] = 47 "crash_reporter-user-collection"; 48static const char kCorePatternProperty[] = "crash_reporter.coredump.enabled"; 49static const char kCoreToMinidumpConverterPath[] = "/system/bin/core2md"; 50 51static const char kStatePrefix[] = "State:\t"; 52 53static const char kCoreTempFolder[] = "/data/misc/crash_reporter/tmp"; 54 55// Define an otherwise invalid value that represents an unknown UID and GID. 56static const uid_t kUnknownUid = -1; 57static const gid_t kUnknownGid = -1; 58 59const char *UserCollector::kUserId = "Uid:\t"; 60const char *UserCollector::kGroupId = "Gid:\t"; 61 62// The property containing the OS version. 63const char kVersionProperty[] = "ro.build.id"; 64 65// The property containing the product id. 66const char kProductIDProperty[] = "ro.product.product_id"; 67 68static const char kBdkVersionKey[] = "bdk_version"; 69 70 71using base::FilePath; 72using base::StringPrintf; 73 74UserCollector::UserCollector() 75 : generate_diagnostics_(false), 76 initialized_(false) { 77} 78 79void UserCollector::Initialize( 80 UserCollector::CountCrashFunction count_crash_function, 81 const std::string &our_path, 82 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function, 83 bool generate_diagnostics, 84 bool core2md_failure, 85 bool directory_failure, 86 const std::string &filter_in) { 87 CrashCollector::Initialize(count_crash_function, 88 is_feedback_allowed_function); 89 our_path_ = our_path; 90 initialized_ = true; 91 generate_diagnostics_ = generate_diagnostics; 92 core2md_failure_ = core2md_failure; 93 directory_failure_ = directory_failure; 94 filter_in_ = filter_in; 95 96 gid_t groups[] = { AID_ROOT, AID_SYSTEM, AID_DBUS }; 97 if (setgroups(arraysize(groups), groups) != 0) { 98 PLOG(FATAL) << "Unable to set groups to root, system, and dbus"; 99 } 100} 101 102UserCollector::~UserCollector() { 103} 104 105std::string UserCollector::GetErrorTypeSignature(ErrorType error_type) const { 106 switch (error_type) { 107 case kErrorSystemIssue: 108 return "system-issue"; 109 case kErrorReadCoreData: 110 return "read-core-data"; 111 case kErrorUnusableProcFiles: 112 return "unusable-proc-files"; 113 case kErrorInvalidCoreFile: 114 return "invalid-core-file"; 115 case kErrorUnsupported32BitCoreFile: 116 return "unsupported-32bit-core-file"; 117 case kErrorCore2MinidumpConversion: 118 return "core2md-conversion"; 119 default: 120 return ""; 121 } 122} 123 124bool UserCollector::SetUpInternal(bool enabled) { 125 CHECK(initialized_); 126 LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling"; 127 128 property_set(kCorePatternProperty, enabled ? "1" : "0"); 129 130 return true; 131} 132 133bool UserCollector::GetFirstLineWithPrefix( 134 const std::vector<std::string> &lines, 135 const char *prefix, std::string *line) { 136 std::vector<std::string>::const_iterator line_iterator; 137 for (line_iterator = lines.begin(); line_iterator != lines.end(); 138 ++line_iterator) { 139 if (line_iterator->find(prefix) == 0) { 140 *line = *line_iterator; 141 return true; 142 } 143 } 144 return false; 145} 146 147bool UserCollector::GetIdFromStatus( 148 const char *prefix, IdKind kind, 149 const std::vector<std::string> &status_lines, int *id) { 150 // From fs/proc/array.c:task_state(), this file contains: 151 // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n 152 std::string id_line; 153 if (!GetFirstLineWithPrefix(status_lines, prefix, &id_line)) { 154 return false; 155 } 156 std::string id_substring = id_line.substr(strlen(prefix), std::string::npos); 157 std::vector<std::string> ids; 158 base::SplitString(id_substring, '\t', &ids); 159 if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) { 160 return false; 161 } 162 const char *number = ids[kind].c_str(); 163 char *end_number = nullptr; 164 *id = strtol(number, &end_number, 10); 165 if (*end_number != '\0') { 166 return false; 167 } 168 return true; 169} 170 171bool UserCollector::GetStateFromStatus( 172 const std::vector<std::string> &status_lines, std::string *state) { 173 std::string state_line; 174 if (!GetFirstLineWithPrefix(status_lines, kStatePrefix, &state_line)) { 175 return false; 176 } 177 *state = state_line.substr(strlen(kStatePrefix), std::string::npos); 178 return true; 179} 180 181void UserCollector::EnqueueCollectionErrorLog(pid_t pid, 182 ErrorType error_type, 183 const std::string &exec) { 184 FilePath crash_path; 185 LOG(INFO) << "Writing conversion problems as separate crash report."; 186 if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, nullptr)) { 187 LOG(ERROR) << "Could not even get log directory; out of space?"; 188 return; 189 } 190 AddCrashMetaData("sig", kCollectionErrorSignature); 191 AddCrashMetaData("error_type", GetErrorTypeSignature(error_type)); 192 std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid); 193 std::string error_log = brillo::GetLog(); 194 FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog"); 195 if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature, 196 diag_log_path)) { 197 // We load the contents of diag_log into memory and append it to 198 // the error log. We cannot just append to files because we need 199 // to always create new files to prevent attack. 200 std::string diag_log_contents; 201 base::ReadFileToString(diag_log_path, &diag_log_contents); 202 error_log.append(diag_log_contents); 203 base::DeleteFile(diag_log_path, false); 204 } 205 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); 206 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); 207 // We must use WriteNewFile instead of base::WriteFile as we do 208 // not want to write with root access to a symlink that an attacker 209 // might have created. 210 if (WriteNewFile(log_path, error_log.data(), error_log.length()) < 0) { 211 LOG(ERROR) << "Error writing new file " << log_path.value(); 212 return; 213 } 214 WriteCrashMetaData(meta_path, exec, log_path.value()); 215} 216 217bool UserCollector::CopyOffProcFiles(pid_t pid, 218 const FilePath &container_dir) { 219 if (!base::CreateDirectory(container_dir)) { 220 PLOG(ERROR) << "Could not create " << container_dir.value(); 221 return false; 222 } 223 int dir_mask = base::FILE_PERMISSION_READ_BY_USER 224 | base::FILE_PERMISSION_WRITE_BY_USER 225 | base::FILE_PERMISSION_EXECUTE_BY_USER 226 | base::FILE_PERMISSION_READ_BY_GROUP 227 | base::FILE_PERMISSION_WRITE_BY_GROUP; 228 if (!base::SetPosixFilePermissions(container_dir, 229 base::FILE_PERMISSION_MASK & dir_mask)) { 230 PLOG(ERROR) << "Could not set permissions for " << container_dir.value() 231 << " to " << std::oct 232 << (base::FILE_PERMISSION_MASK & dir_mask); 233 return false; 234 } 235 FilePath process_path = GetProcessPath(pid); 236 if (!base::PathExists(process_path)) { 237 LOG(ERROR) << "Path " << process_path.value() << " does not exist"; 238 return false; 239 } 240 static const char *proc_files[] = { 241 "auxv", 242 "cmdline", 243 "environ", 244 "maps", 245 "status" 246 }; 247 for (unsigned i = 0; i < arraysize(proc_files); ++i) { 248 if (!base::CopyFile(process_path.Append(proc_files[i]), 249 container_dir.Append(proc_files[i]))) { 250 LOG(ERROR) << "Could not copy " << proc_files[i] << " file"; 251 return false; 252 } 253 } 254 return true; 255} 256 257bool UserCollector::ValidateProcFiles(const FilePath &container_dir) const { 258 // Check if the maps file is empty, which could be due to the crashed 259 // process being reaped by the kernel before finishing a core dump. 260 int64_t file_size = 0; 261 if (!base::GetFileSize(container_dir.Append("maps"), &file_size)) { 262 LOG(ERROR) << "Could not get the size of maps file"; 263 return false; 264 } 265 if (file_size == 0) { 266 LOG(ERROR) << "maps file is empty"; 267 return false; 268 } 269 return true; 270} 271 272UserCollector::ErrorType UserCollector::ValidateCoreFile( 273 const FilePath &core_path) const { 274 int fd = HANDLE_EINTR(open(core_path.value().c_str(), O_RDONLY)); 275 if (fd < 0) { 276 PLOG(ERROR) << "Could not open core file " << core_path.value(); 277 return kErrorInvalidCoreFile; 278 } 279 280 char e_ident[EI_NIDENT]; 281 bool read_ok = base::ReadFromFD(fd, e_ident, sizeof(e_ident)); 282 IGNORE_EINTR(close(fd)); 283 if (!read_ok) { 284 LOG(ERROR) << "Could not read header of core file"; 285 return kErrorInvalidCoreFile; 286 } 287 288 if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || 289 e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) { 290 LOG(ERROR) << "Invalid core file"; 291 return kErrorInvalidCoreFile; 292 } 293 294#if __WORDSIZE == 64 295 // TODO(benchan, mkrebs): Remove this check once core2md can 296 // handles both 32-bit and 64-bit ELF on a 64-bit platform. 297 if (e_ident[EI_CLASS] == ELFCLASS32) { 298 LOG(ERROR) << "Conversion of 32-bit core file on 64-bit platform is " 299 << "currently not supported"; 300 return kErrorUnsupported32BitCoreFile; 301 } 302#endif 303 304 return kErrorNone; 305} 306 307bool UserCollector::GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid, 308 FilePath *crash_file_path, 309 bool *out_of_capacity) { 310 FilePath process_path = GetProcessPath(pid); 311 std::string status; 312 if (directory_failure_) { 313 LOG(ERROR) << "Purposefully failing to create spool directory"; 314 return false; 315 } 316 317 uid_t uid; 318 if (base::ReadFileToString(process_path.Append("status"), &status)) { 319 std::vector<std::string> status_lines; 320 base::SplitString(status, '\n', &status_lines); 321 322 std::string process_state; 323 if (!GetStateFromStatus(status_lines, &process_state)) { 324 LOG(ERROR) << "Could not find process state in status file"; 325 return false; 326 } 327 LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state; 328 329 // Get effective UID of crashing process. 330 int id; 331 if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &id)) { 332 LOG(ERROR) << "Could not find euid in status file"; 333 return false; 334 } 335 uid = id; 336 } else if (supplied_ruid != kUnknownUid) { 337 LOG(INFO) << "Using supplied UID " << supplied_ruid 338 << " for crashed process [" << pid 339 << "] due to error reading status file"; 340 uid = supplied_ruid; 341 } else { 342 LOG(ERROR) << "Could not read status file and kernel did not supply UID"; 343 LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: " 344 << base::DirectoryExists(process_path); 345 return false; 346 } 347 348 if (!GetCreatedCrashDirectoryByEuid(uid, crash_file_path, out_of_capacity)) { 349 LOG(ERROR) << "Could not create crash directory"; 350 return false; 351 } 352 return true; 353} 354 355bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { 356 // Copy off all stdin to a core file. 357 FilePath stdin_path("/proc/self/fd/0"); 358 if (base::CopyFile(stdin_path, core_path)) { 359 return true; 360 } 361 362 PLOG(ERROR) << "Could not write core file"; 363 // If the file system was full, make sure we remove any remnants. 364 base::DeleteFile(core_path, false); 365 return false; 366} 367 368bool UserCollector::RunCoreToMinidump(const FilePath &core_path, 369 const FilePath &procfs_directory, 370 const FilePath &minidump_path, 371 const FilePath &temp_directory) { 372 FilePath output_path = temp_directory.Append("output"); 373 brillo::ProcessImpl core2md; 374 core2md.RedirectOutput(output_path.value()); 375 core2md.AddArg(kCoreToMinidumpConverterPath); 376 core2md.AddArg(core_path.value()); 377 core2md.AddArg(procfs_directory.value()); 378 379 if (!core2md_failure_) { 380 core2md.AddArg(minidump_path.value()); 381 } else { 382 // To test how core2md errors are propagaged, cause an error 383 // by forgetting a required argument. 384 } 385 386 int errorlevel = core2md.Run(); 387 388 std::string output; 389 base::ReadFileToString(output_path, &output); 390 if (errorlevel != 0) { 391 LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath 392 << " [result=" << errorlevel << "]: " << output; 393 return false; 394 } 395 396 if (!base::PathExists(minidump_path)) { 397 LOG(ERROR) << "Minidump file " << minidump_path.value() 398 << " was not created"; 399 return false; 400 } 401 return true; 402} 403 404UserCollector::ErrorType UserCollector::ConvertCoreToMinidump( 405 pid_t pid, 406 const FilePath &container_dir, 407 const FilePath &core_path, 408 const FilePath &minidump_path) { 409 // If proc files are unuable, we continue to read the core file from stdin, 410 // but only skip the core-to-minidump conversion, so that we may still use 411 // the core file for debugging. 412 bool proc_files_usable = 413 CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir); 414 415 // Switch back to the original UID/GID. 416 gid_t rgid, egid, sgid; 417 if (getresgid(&rgid, &egid, &sgid) != 0) { 418 PLOG(FATAL) << "Unable to read saved gid"; 419 } 420 if (setresgid(sgid, sgid, -1) != 0) { 421 PLOG(FATAL) << "Unable to set real group ID back to saved gid"; 422 } else { 423 if (getresgid(&rgid, &egid, &sgid) != 0) { 424 // If the groups cannot be read at this point, the rgid variable will 425 // contain the previously read group ID from before changing it. This 426 // will cause the chown call below to set the incorrect group for 427 // non-root crashes. But do not treat this as a fatal error, so that 428 // the rest of the collection will continue for potential manual 429 // collection by a developer. 430 PLOG(ERROR) << "Unable to read real group ID after setting it"; 431 } 432 } 433 434 uid_t ruid, euid, suid; 435 if (getresuid(&ruid, &euid, &suid) != 0) { 436 PLOG(FATAL) << "Unable to read saved uid"; 437 } 438 if (setresuid(suid, suid, -1) != 0) { 439 PLOG(FATAL) << "Unable to set real user ID back to saved uid"; 440 } else { 441 if (getresuid(&ruid, &euid, &suid) != 0) { 442 // If the user ID cannot be read at this point, the ruid variable will 443 // contain the previously read user ID from before changing it. This 444 // will cause the chown call below to set the incorrect user for 445 // non-root crashes. But do not treat this as a fatal error, so that 446 // the rest of the collection will continue for potential manual 447 // collection by a developer. 448 PLOG(ERROR) << "Unable to read real user ID after setting it"; 449 } 450 } 451 452 if (!CopyStdinToCoreFile(core_path)) { 453 return kErrorReadCoreData; 454 } 455 456 if (!proc_files_usable) { 457 LOG(INFO) << "Skipped converting core file to minidump due to " 458 << "unusable proc files"; 459 return kErrorUnusableProcFiles; 460 } 461 462 ErrorType error = ValidateCoreFile(core_path); 463 if (error != kErrorNone) { 464 return error; 465 } 466 467 // Chown the temp container directory back to the original user/group that 468 // crash_reporter is run as, so that additional files can be written to 469 // the temp folder. 470 if (chown(container_dir.value().c_str(), ruid, rgid) < 0) { 471 PLOG(ERROR) << "Could not set owner for " << container_dir.value(); 472 } 473 474 if (!RunCoreToMinidump(core_path, 475 container_dir, // procfs directory 476 minidump_path, 477 container_dir)) { // temporary directory 478 return kErrorCore2MinidumpConversion; 479 } 480 481 LOG(INFO) << "Stored minidump to " << minidump_path.value(); 482 return kErrorNone; 483} 484 485UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash( 486 pid_t pid, const std::string &exec, uid_t supplied_ruid, 487 bool *out_of_capacity) { 488 FilePath crash_path; 489 if (!GetCreatedCrashDirectory(pid, supplied_ruid, &crash_path, 490 out_of_capacity)) { 491 LOG(ERROR) << "Unable to find/create process-specific crash path"; 492 return kErrorSystemIssue; 493 } 494 495 // Directory like /tmp/crash_reporter/1234 which contains the 496 // procfs entries and other temporary files used during conversion. 497 FilePath container_dir(StringPrintf("%s/%d", kCoreTempFolder, pid)); 498 // Delete a pre-existing directory from crash reporter that may have 499 // been left around for diagnostics from a failed conversion attempt. 500 // If we don't, existing files can cause forking to fail. 501 base::DeleteFile(container_dir, true); 502 std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid); 503 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core"); 504 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); 505 FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp"); 506 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); 507 508 if (GetLogContents(FilePath(log_config_path_), exec, log_path)) 509 AddCrashMetaData("log", log_path.value()); 510 511 char value[PROPERTY_VALUE_MAX]; 512 property_get(kVersionProperty, value, "undefined"); 513 AddCrashMetaUploadData("ver", value); 514 property_get(kProductIDProperty, value, "undefined"); 515 AddCrashMetaUploadData("prod", value); 516 517 brillo::OsReleaseReader reader; 518 reader.Load(); 519 std::string bdk_version = "undefined"; 520 if (!reader.GetString(kBdkVersionKey, &bdk_version)) { 521 LOG(ERROR) << "Could not read " << kBdkVersionKey 522 << " from /etc/os-release.d/"; 523 } 524 AddCrashMetaData(kBdkVersionKey, bdk_version); 525 526 ErrorType error_type = 527 ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path); 528 if (error_type != kErrorNone) { 529 LOG(INFO) << "Leaving core file at " << core_path.value() 530 << " due to conversion error"; 531 return error_type; 532 } 533 534 // Here we commit to sending this file. We must not return false 535 // after this point or we will generate a log report as well as a 536 // crash report. 537 WriteCrashMetaData(meta_path, 538 exec, 539 minidump_path.value()); 540 541 if (!IsDeveloperImage()) { 542 base::DeleteFile(core_path, false); 543 } else { 544 LOG(INFO) << "Leaving core file at " << core_path.value() 545 << " due to developer image"; 546 } 547 548 base::DeleteFile(container_dir, true); 549 return kErrorNone; 550} 551 552bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes, 553 pid_t *pid, int *signal, uid_t *uid, 554 gid_t *gid, 555 std::string *kernel_supplied_name) { 556 pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(\\d+):(.*)"); 557 if (re.FullMatch(crash_attributes, pid, signal, uid, gid, 558 kernel_supplied_name)) 559 return true; 560 561 LOG(INFO) << "Falling back to parsing crash attributes '" 562 << crash_attributes << "' without UID and GID"; 563 pcrecpp::RE re_without_uid("(\\d+):(\\d+):(.*)"); 564 *uid = kUnknownUid; 565 *gid = kUnknownGid; 566 return re_without_uid.FullMatch(crash_attributes, pid, signal, 567 kernel_supplied_name); 568} 569 570bool UserCollector::ShouldDump(bool has_owner_consent, 571 bool is_developer, 572 std::string *reason) { 573 reason->clear(); 574 575 // For developer builds, we always want to keep the crash reports unless 576 // we're testing the crash facilities themselves. This overrides 577 // feedback. Crash sending still obeys consent. 578 if (is_developer) { 579 *reason = "developer build - not testing - always dumping"; 580 return true; 581 } 582 583 if (!has_owner_consent) { 584 *reason = "ignoring - no consent"; 585 return false; 586 } 587 588 *reason = "handling"; 589 return true; 590} 591 592bool UserCollector::HandleCrash(const std::string &crash_attributes, 593 const char *force_exec) { 594 CHECK(initialized_); 595 pid_t pid = 0; 596 int signal = 0; 597 uid_t supplied_ruid = kUnknownUid; 598 gid_t supplied_rgid = kUnknownGid; 599 std::string kernel_supplied_name; 600 601 if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &supplied_ruid, 602 &supplied_rgid, &kernel_supplied_name)) { 603 LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes; 604 return false; 605 } 606 607 // Switch to the group and user that ran the crashing binary in order to 608 // access their /proc files. Do not set suid/sgid, so that we can switch 609 // back after copying the necessary files. 610 if (setresgid(supplied_rgid, supplied_rgid, -1) != 0) { 611 PLOG(FATAL) << "Unable to set real group ID to access process files"; 612 } 613 if (setresuid(supplied_ruid, supplied_ruid, -1) != 0) { 614 PLOG(FATAL) << "Unable to set real user ID to access process files"; 615 } 616 617 std::string exec; 618 if (force_exec) { 619 exec.assign(force_exec); 620 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) { 621 // If we cannot find the exec name, use the kernel supplied name. 622 // We don't always use the kernel's since it truncates the name to 623 // 16 characters. 624 exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str()); 625 } 626 627 // Allow us to test the crash reporting mechanism successfully even if 628 // other parts of the system crash. 629 if (!filter_in_.empty() && 630 (filter_in_ == "none" || 631 filter_in_ != exec)) { 632 // We use a different format message to make it more obvious in tests 633 // which crashes are test generated and which are real. 634 LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while " 635 << "filter_in=" << filter_in_ << "."; 636 return true; 637 } 638 639 std::string reason; 640 bool dump = ShouldDump(is_feedback_allowed_function_(), 641 IsDeveloperImage(), 642 &reason); 643 644 LOG(WARNING) << "Received crash notification for " << exec << "[" << pid 645 << "] sig " << signal << ", user " << supplied_ruid 646 << " (" << reason << ")"; 647 648 if (dump) { 649 count_crash_function_(); 650 651 if (generate_diagnostics_) { 652 bool out_of_capacity = false; 653 ErrorType error_type = 654 ConvertAndEnqueueCrash(pid, exec, supplied_ruid, &out_of_capacity); 655 if (error_type != kErrorNone) { 656 if (!out_of_capacity) 657 EnqueueCollectionErrorLog(pid, error_type, exec); 658 return false; 659 } 660 } 661 } 662 663 return true; 664} 665