From e4a86037f405415c460ace194fbf16cfd93d4c6f Mon Sep 17 00:00:00 2001 From: Chris Sosa Date: Wed, 16 Jun 2010 17:08:34 -0700 Subject: [PATCH 001/225] Initial commit of crash reporter repo --- crash_reporter/Makefile | 38 ++++ crash_reporter/crash_reporter.cc | 127 +++++++++++++ crash_reporter/crash_sender | 206 ++++++++++++++++++++++ crash_reporter/crash_sender.hourly | 11 ++ crash_reporter/inherit-review-settings-ok | 0 crash_reporter/make_tests.sh | 16 ++ crash_reporter/system_logging.cc | 44 +++++ crash_reporter/system_logging.h | 30 ++++ crash_reporter/system_logging_mock.cc | 35 ++++ crash_reporter/system_logging_mock.h | 27 +++ crash_reporter/user_collector.cc | 76 ++++++++ crash_reporter/user_collector.h | 62 +++++++ crash_reporter/user_collector_test.cc | 102 +++++++++++ 13 files changed, 774 insertions(+) create mode 100644 crash_reporter/Makefile create mode 100644 crash_reporter/crash_reporter.cc create mode 100644 crash_reporter/crash_sender create mode 100644 crash_reporter/crash_sender.hourly create mode 100644 crash_reporter/inherit-review-settings-ok create mode 100755 crash_reporter/make_tests.sh create mode 100644 crash_reporter/system_logging.cc create mode 100644 crash_reporter/system_logging.h create mode 100644 crash_reporter/system_logging_mock.cc create mode 100644 crash_reporter/system_logging_mock.h create mode 100644 crash_reporter/user_collector.cc create mode 100644 crash_reporter/user_collector.h create mode 100644 crash_reporter/user_collector_test.cc diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile new file mode 100644 index 000000000..1b78f2730 --- /dev/null +++ b/crash_reporter/Makefile @@ -0,0 +1,38 @@ +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +CRASH_BIN = crash_reporter +CRASH_LIB = libcrash.so +CRASH_OBJS = system_logging.o user_collector.o +TEST_OBJS = $(CRASH_OBJS) system_logging_mock.o +TEST_BINS = user_collector_test + +LIBS = -lbase -lpthread -lgflags -lrt -lmetrics + +TEST_LIBS = $(LIBS) -lgtest -lgmock +INCLUDE_DIRS = -I.. -I$(SYSROOT)/usr/include/google-breakpad +LIB_DIRS = + +# We need -fPIC for linking objects into shared objects. +CXXFLAGS += -fPIC -Wall -Werror + +all: + echo "Specify either $(CRASH_BIN) or $(CRASH_LIB)" + +$(CRASH_LIB): crash_dumper.o + $(CXX) -shared -lbreakpad_client $^ -o $@ + +$(CRASH_BIN): crash_reporter.o $(CRASH_OBJS) + $(CXX) $(CXXFLAGS) $(LIB_DIRS) $^ -lcrash $(LIBS) -o $@ + +tests: $(TEST_BINS) + +user_collector_test: user_collector_test.o $(TEST_OBJS) + $(CXX) $(CXXFLAGS) $(LIB_DIRS) $^ $(TEST_LIBS) -o $@ + +.cc.o: + $(CXX) $(CXXFLAGS) $(INCLUDE_DIRS) -c $< -o $@ + +clean: + rm -rf *.o $(CRASH_BIN) $(TEST_BINS) $(CRASH_LIB) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc new file mode 100644 index 000000000..951d3fae3 --- /dev/null +++ b/crash_reporter/crash_reporter.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "crash/system_logging.h" +#include "crash/user_collector.h" +#include "gflags/gflags.h" +#include "metrics/metrics_library.h" + +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +DEFINE_bool(init, false, "Initialize crash logging"); +DEFINE_bool(clean_shutdown, false, "Signal clean shutdown"); +DEFINE_bool(crash_test, false, "Crash test"); +DEFINE_string(exec, "", "Executable name crashed"); +DEFINE_int32(pid, -1, "Crashing PID"); +DEFINE_int32(signal, -1, "Signal causing crash"); +DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); +#pragma GCC diagnostic error "-Wstrict-aliasing" + +static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; +static const char kUncleanShutdownFile[] = + "/var/lib/crash_reporter/pending_clean_shutdown"; + +// Enumeration of kinds of crashes to be used in the CrashCounter histogram. +enum CrashKinds { + CRASH_KIND_KERNEL = 1, + CRASH_KIND_USER = 2, + CRASH_KIND_MAX +}; + +static MetricsLibrary s_metrics_lib; +static SystemLoggingImpl s_system_log; + +static bool IsMetricsCollectionAllowed() { + // TODO(kmixter): Eventually check system tainted state and + // move this down in metrics library where it would be explicitly + // checked when asked to send stats. + return true; +} + +static void CheckUncleanShutdown() { + FilePath unclean_file_path(kUncleanShutdownFile); + if (!file_util::PathExists(unclean_file_path)) { + return; + } + s_system_log.LogWarning("Last shutdown was not clean"); + if (IsMetricsCollectionAllowed()) { + s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), + CRASH_KIND_KERNEL, + CRASH_KIND_MAX); + } + if (!file_util::Delete(unclean_file_path, false)) { + s_system_log.LogError("Failed to delete unclean shutdown file %s", + kUncleanShutdownFile); + } +} + +static bool PrepareUncleanShutdownCheck() { + static const char empty[] = ""; + FilePath file_path(kUncleanShutdownFile); + file_util::CreateDirectory(file_path.DirName()); + return file_util::WriteFile(file_path, empty, 0) == 0; +} + +static void SignalCleanShutdown() { + s_system_log.LogInfo("Clean shutdown signalled"); + file_util::Delete(FilePath(kUncleanShutdownFile), false); +} + +static void CountUserCrash() { + CHECK(IsMetricsCollectionAllowed()); + s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), + CRASH_KIND_USER, + CRASH_KIND_MAX); +} + +int main(int argc, char *argv[]) { + google::ParseCommandLineFlags(&argc, &argv, true); + FilePath my_path(argv[0]); + file_util::AbsolutePath(&my_path); + s_metrics_lib.Init(); + s_system_log.Initialize(my_path.BaseName().value().c_str()); + UserCollector user_collector; + user_collector.Initialize(CountUserCrash, + my_path.value(), + IsMetricsCollectionAllowed, + &s_system_log); + + if (FLAGS_init) { + CHECK(!FLAGS_clean_shutdown) << "Incompatible options"; + user_collector.Enable(); + if (FLAGS_unclean_check) { + CheckUncleanShutdown(); + if (!PrepareUncleanShutdownCheck()) { + s_system_log.LogError("Unable to create shutdown check file"); + } + } + return 0; + } + + if (FLAGS_clean_shutdown) { + SignalCleanShutdown(); + user_collector.Disable(); + return 0; + } + + // Handle a specific user space crash. + CHECK(FLAGS_signal != -1) << "Signal must be set"; + CHECK(FLAGS_pid != -1) << "PID must be set"; + CHECK(FLAGS_exec != "") << "Executable name must be set"; + + // Make it possible to test what happens when we crash while + // handling a crash. + if (FLAGS_crash_test) { + *(char *)0 = 0; + return 0; + } + + user_collector.HandleCrash(FLAGS_signal, FLAGS_pid, FLAGS_exec); + + return 0; +} diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender new file mode 100644 index 000000000..4332117ae --- /dev/null +++ b/crash_reporter/crash_sender @@ -0,0 +1,206 @@ +#!/bin/sh + +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +# Product ID in crash report +CHROMEOS_PRODUCT=ChromeOS + +# Send up to 8 crashes per day. +MAX_CRASH_RATE=8 + +# Minidump uploading tool (provided by Google Breakpad). +MINIDUMP_UPLOADER=/usr/bin/minidump_upload + +# URL to send non-official build crashes to. +MINIDUMP_UPLOAD_STAGING_URL="http://clients2.google.com/cr/staging_report" + +# URL to send official build crashes to. +MINIDUMP_UPLOAD_PROD_URL="http://clients2.google.com/cr/report" + +# File whose existence mocks crash sending. If empty we pretend the +# crash sending was successful, otherwise unsuccessful. +MOCK_CRASH_SENDING="/tmp/mock-crash-sending" + +# File whose existence causes crash sending to be delayed (for testing). +PAUSE_CRASH_SENDING="/tmp/pause-crash-sending" + +# File whose existence implies we're running and not to start again. +RUN_FILE="/var/run/crash_sender.pid" + +# Maximum time to sleep between sends. +SECONDS_SEND_SPREAD=600 + +# The syslog tag for all logging we emit. +TAG="$(basename $0)[$$]" + +# Directory to store timestamp files indicating the uploads in the past 24 +# hours. +TIMESTAMPS_DIR="/var/lib/crash_sender" + +lecho() { + logger -t "${TAG}" "$@" +} + +remove_run_file() { + rm -f "${RUN_FILE}" +} + +check_not_already_running() { + if [ ! -f "${RUN_FILE}" ]; then + return + fi + local last_pid=$(cat "${RUN_FILE}") + if [ ! -f "/proc/${last_pid}/cmdline" ]; then + trap remove_run_file EXIT + echo $$ > "${RUN_FILE}" + return + fi + # This could just be an unrelated process, but it's ok to be conservative. + lecho "Already running. Exiting now." + exit 1 +} + +get_version() { + grep ^CHROMEOS_RELEASE_VERSION /etc/lsb-release | cut -d = -f 2- +} + +is_official() { + grep ^CHROMEOS_RELEASE_DESCRIPTION /etc/lsb-release | cut -d = -f 2- | \ + grep Official +} + +# Generate a uniform random number in 0..max-1. +generate_uniform_random() { + local max=$1 + local random="$(od -An -N4 -tu /dev/urandom)" + echo $((random % max)) +} + +is_feedback_disabled() { + # See crosbug.com/3303. + return 1 +} + +is_on_3g() { + # See crosbug.com/3304. + return 1 +} + +# Check if sending a crash now does not exceed the maximum 24hr rate and +# commit to doing so, if not. +check_rate() { + mkdir -p ${TIMESTAMPS_DIR} + # Only consider minidumps written in the past 24 hours by removing all older. + find "${TIMESTAMPS_DIR}" -mindepth 1 -mmin +$((24 * 60)) -exec rm '{}' ';' + local sends_in_24hrs=$(echo "${TIMESTAMPS_DIR}"/* | wc -w) + lecho "Current send rate: ${sends_in_24hrs}sends/24hrs" + if [ ${sends_in_24hrs} -ge ${MAX_CRASH_RATE} ]; then + lecho "Cannot send more crashes:" + lecho " current ${sends_in_24hrs}send/24hrs >= " \ + "max ${MAX_CRASH_RATE}send/24hrs" + return 1 + fi + mktemp "${TIMESTAMPS_DIR}"/XXXX > /dev/null + return 0 +} + +send_crash() { + local sleep_time=$(generate_uniform_random $SECONDS_SEND_SPREAD) + local url="${MINIDUMP_UPLOAD_STAGING_URL}" + if is_official; then + url="${MINIDUMP_UPLOAD_PROD_URL}" + fi + lecho "Sending crash:" + lecho " Scheduled to send in ${sleep_time}s" + lecho " Minidump: ${minidump_path}" + lecho " URL: ${url}" + lecho " Product: ${CHROMEOS_PRODUCT}" + lecho " Version: ${chromeos_version}" + if [ -s "${minidump_path}" ]; then + # We cannot tell much from the minidump without symbols, but we can tell + # at least what modules were loaded at the time of crash + local modules="$(/usr/bin/minidump_dump "${minidump_path}" 2>&- | \ + grep 'code_file' | sed -e 's/^.* = "//g;s/"//g' | \ + tr '\n' ' ')" + lecho " Mapped: ${modules}" + fi + if [ -f "${MOCK_CRASH_SENDING}" ]; then + local mock_in=$(cat "${MOCK_CRASH_SENDING}") + if [ "${mock_in}" = "" ]; then + lecho "Mocking successful send" + return 0 + else + lecho "Mocking unsuccessful send" + return 1 + fi + fi + + if ! sleep ${sleep_time}; then + lecho "Sleep failed" + return 1 + fi + + "${MINIDUMP_UPLOADER}" -p "${CHROMEOS_PRODUCT}" \ + -v "${chromeos_version}" "${minidump_path}" "${url}" + return $? +} + +# Send all crashes from the given directory. The directory is currently +# expected to just contain a bunch of minidumps - but this will change +# over time to be a directory of directories where the minidump and core +# file are in the directory as well as other metadata about the context +# of the crash (executable name for instance). +send_crashes() { + local dir="$1" + lecho "Considering crashes in ${dir}" + # Cycle through minidumps, most recent first. That way if we're about + # to exceed the daily rate, we send the most recent minidumps. + if [ ! -d "${dir}" ]; then + return + fi + for file in $(ls -1t "${dir}"); do + local minidump_path="${dir}/${file}" + lecho "Considering crash ${minidump_path}" + if ! check_rate; then + lecho "Sending ${minidump_path} would exceed rate. Leaving for later." + return 0 + fi + local chromeos_version=$(get_version) + if is_feedback_disabled; then + lecho "Uploading is disabled. Removing crash." + rm "${minidump_path}" + elif is_on_3g; then + lecho "Not sending crash report while on 3G, saving for later." + elif send_crash ${minidump_path}; then + # Send was successful, now remove + lecho "Successfully sent crash ${minidump_path} and removing" + rm "${minidump_path}" + else + lecho "Problem sending ${minidump_path}, not removing" + fi + done +} + +main() { + lecho "Starting" + if [ -e "${PAUSE_CRASH_SENDING}" ]; then + lecho "Exiting early due to ${PAUSE_CRASH_SENDING}" + exit 1 + fi + + check_not_already_running + + # Send system-wide crashes + send_crashes "/var/spool/crash" + + # Send user-specific crashes + send_crashes "/home/chronos/user/crash" + + lecho "Done" +} + +main diff --git a/crash_reporter/crash_sender.hourly b/crash_reporter/crash_sender.hourly new file mode 100644 index 000000000..09c815193 --- /dev/null +++ b/crash_reporter/crash_sender.hourly @@ -0,0 +1,11 @@ +#!/bin/sh + +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +# Background the sender so it can trickle out crash dumps. It will +# exit early if already running. +/sbin/crash_sender 2>&1 & diff --git a/crash_reporter/inherit-review-settings-ok b/crash_reporter/inherit-review-settings-ok new file mode 100644 index 000000000..e69de29bb diff --git a/crash_reporter/make_tests.sh b/crash_reporter/make_tests.sh new file mode 100755 index 000000000..609069ec9 --- /dev/null +++ b/crash_reporter/make_tests.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Builds tests. + +set -e + +SOURCE_DIR=$(readlink -f $(dirname $0)) +pushd "$SCRIPT_DIR" +make tests +mkdir -p "${OUT_DIR}" +cp *_test "${OUT_DIR}" +popd diff --git a/crash_reporter/system_logging.cc b/crash_reporter/system_logging.cc new file mode 100644 index 000000000..e366ef2c2 --- /dev/null +++ b/crash_reporter/system_logging.cc @@ -0,0 +1,44 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "crash/system_logging.h" + +std::string SystemLoggingImpl::identity_; + +SystemLoggingImpl::SystemLoggingImpl() { +} + +SystemLoggingImpl::~SystemLoggingImpl() { +} + +void SystemLoggingImpl::Initialize(const char *ident) { + // Man page does not specify if openlog copies its string or assumes + // the pointer is always valid, so make its scope global. + identity_ = ident; + openlog(identity_.c_str(), LOG_PID, LOG_USER); +} + +void SystemLoggingImpl::LogInfo(const char *format, ...) { + va_list vl; + va_start(vl, format); + vsyslog(LOG_INFO, format, vl); + va_end(vl); +} + +void SystemLoggingImpl::LogWarning(const char *format, ...) { + va_list vl; + va_start(vl, format); + vsyslog(LOG_WARNING, format, vl); + va_end(vl); +} + +void SystemLoggingImpl::LogError(const char *format, ...) { + va_list vl; + va_start(vl, format); + vsyslog(LOG_ERR, format, vl); + va_end(vl); +} diff --git a/crash_reporter/system_logging.h b/crash_reporter/system_logging.h new file mode 100644 index 000000000..8c946b01a --- /dev/null +++ b/crash_reporter/system_logging.h @@ -0,0 +1,30 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRASH_SYSTEM_LOGGING_H_ +#define CRASH_SYSTEM_LOGGING_H_ + +#include + +class SystemLogging { + public: + virtual void Initialize(const char *ident) = 0; + virtual void LogInfo(const char *format, ...) = 0; + virtual void LogWarning(const char *format, ...) = 0; + virtual void LogError(const char *format, ...) = 0; +}; + +class SystemLoggingImpl : public SystemLogging { + public: + SystemLoggingImpl(); + virtual ~SystemLoggingImpl(); + virtual void Initialize(const char *ident); + virtual void LogInfo(const char *format, ...); + virtual void LogWarning(const char *format, ...); + virtual void LogError(const char *format, ...); + private: + static std::string identity_; +}; + +#endif // CRASH_SYSTEM_LOGGING_H_ diff --git a/crash_reporter/system_logging_mock.cc b/crash_reporter/system_logging_mock.cc new file mode 100644 index 000000000..3da195620 --- /dev/null +++ b/crash_reporter/system_logging_mock.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/string_util.h" +#include "crash/system_logging_mock.h" + +void SystemLoggingMock::LogInfo(const char *format, ...) { + va_list vl; + va_start(vl, format); + log_ += ident_ + "info: "; + StringAppendV(&log_, format, vl); + log_ += "\n"; + va_end(vl); +} + +void SystemLoggingMock::LogWarning(const char *format, ...) { + va_list vl; + va_start(vl, format); + log_ += ident_ + "warning: "; + StringAppendV(&log_, format, vl); + log_ += "\n"; + va_end(vl); +} + +void SystemLoggingMock::LogError(const char *format, ...) { + va_list vl; + va_start(vl, format); + log_ += ident_ + "error: "; + StringAppendV(&log_, format, vl); + log_ += "\n"; + va_end(vl); +} diff --git a/crash_reporter/system_logging_mock.h b/crash_reporter/system_logging_mock.h new file mode 100644 index 000000000..983c72467 --- /dev/null +++ b/crash_reporter/system_logging_mock.h @@ -0,0 +1,27 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRASH_SYSTEM_LOGGING_MOCK_H_ +#define CRASH_SYSTEM_LOGGING_MOCK_H_ + +#include + +#include "crash/system_logging.h" + +class SystemLoggingMock : public SystemLogging { + public: + void Initialize(const char *ident) {} + virtual void LogInfo(const char *format, ...); + virtual void LogWarning(const char *format, ...); + virtual void LogError(const char *format, ...); + + const std::string &log() { return log_; } + + private: + static std::string identity_; + std::string log_; + std::string ident_; +}; + +#endif // CRASH_SYSTEM_LOGGING_H_ diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc new file mode 100644 index 000000000..7033ada11 --- /dev/null +++ b/crash_reporter/user_collector.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "crash/user_collector.h" +#include "metrics/metrics_library.h" + +// This procfs file is used to cause kernel core file writing to +// instead pipe the core file into a user space process. See +// core(5) man page. +static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern"; + +UserCollector::UserCollector() + : core_pattern_file_(kCorePatternFile), + count_crash_function_(NULL), + initialized_(false), + is_feedback_allowed_function_(NULL), + logger_(NULL) { +} + +void UserCollector::Initialize( + UserCollector::CountCrashFunction count_crash_function, + const std::string &our_path, + UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function, + SystemLogging *logger) { + CHECK(count_crash_function != NULL); + CHECK(is_feedback_allowed_function != NULL); + CHECK(logger != NULL); + + count_crash_function_ = count_crash_function; + our_path_ = our_path; + is_feedback_allowed_function_ = is_feedback_allowed_function; + logger_ = logger; + initialized_ = true; +} + +UserCollector::~UserCollector() { +} + +std::string UserCollector::GetPattern(bool enabled) const { + if (enabled) { + return StringPrintf("|%s --signal=%%s --pid=%%p --exec=%%e", + our_path_.c_str()); + } else { + return "core"; + } +} + +bool UserCollector::SetUpInternal(bool enabled) { + CHECK(initialized_); + logger_->LogInfo("%s crash handling", enabled ? "Enabling" : "Disabling"); + std::string pattern = GetPattern(enabled); + if (file_util::WriteFile(FilePath(core_pattern_file_), + pattern.c_str(), + pattern.length()) != + static_cast(pattern.length())) { + logger_->LogError("Unable to write %s", core_pattern_file_.c_str()); + return false; + } + return true; +} + +void UserCollector::HandleCrash(int signal, int pid, const std::string &exec) { + CHECK(initialized_); + logger_->LogWarning("Received crash notification for %s[%d] sig %d", + exec.c_str(), pid, signal); + + if (is_feedback_allowed_function_()) { + count_crash_function_(); + } +} diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h new file mode 100644 index 000000000..7f632246a --- /dev/null +++ b/crash_reporter/user_collector.h @@ -0,0 +1,62 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CRASH_USER_COLLECTOR_H_ +#define _CRASH_USER_COLLECTOR_H_ + +#include + +#include "crash/system_logging.h" + +class FilePath; + +// User crash collector. +class UserCollector { + public: + typedef void (*CountCrashFunction)(); + typedef bool (*IsFeedbackAllowedFunction)(); + + UserCollector(); + + // Initialize the user crash collector for detection of crashes, + // given a crash counting function, the path to this executable, + // metrics collection enabled oracle, and system logger facility. + // Crash detection/reporting is not enabled until Enable is + // called. + void Initialize(CountCrashFunction count_crash, + const std::string &our_path, + IsFeedbackAllowedFunction is_metrics_allowed, + SystemLogging *logger); + + virtual ~UserCollector(); + + // Enable collection. + bool Enable() { return SetUpInternal(true); } + + // Disable collection. + bool Disable() { return SetUpInternal(false); } + + // Handle a specific user crash. + void HandleCrash(int signal, int pid, const std::string &exec); + + // Set (override the default) core file pattern. + void set_core_pattern_file(const std::string &pattern) { + core_pattern_file_ = pattern; + } + + private: + friend class UserCollectorTest; + + std::string GetPattern(bool enabled) const; + bool SetUpInternal(bool enabled); + + std::string core_pattern_file_; + CountCrashFunction count_crash_function_; + std::string our_path_; + bool initialized_; + IsFeedbackAllowedFunction is_feedback_allowed_function_; + SystemLogging *logger_; +}; + +#endif // _CRASH_USER_COLLECTOR_H_ diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc new file mode 100644 index 000000000..6398ce707 --- /dev/null +++ b/crash_reporter/user_collector_test.cc @@ -0,0 +1,102 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "base/file_util.h" +#include "crash/system_logging_mock.h" +#include "crash/user_collector.h" + +int s_crashes = 0; +bool s_metrics = false; + +static const char kFilePath[] = "/my/path"; + +void CountCrash() { + ++s_crashes; +} + +bool IsMetrics() { + return s_metrics; +} + +class UserCollectorTest : public ::testing::Test { + void SetUp() { + s_crashes = 0; + collector_.Initialize(CountCrash, + kFilePath, + IsMetrics, + &logging_); + mkdir("test", 0777); + collector_.set_core_pattern_file("test/core_pattern"); + } + protected: + SystemLoggingMock logging_; + UserCollector collector_; +}; + +TEST_F(UserCollectorTest, EnableOK) { + std::string contents; + ASSERT_TRUE(collector_.Enable()); + ASSERT_TRUE(file_util::ReadFileToString(FilePath("test/core_pattern"), + &contents)); + ASSERT_STREQ(contents.c_str(), + "|/my/path --signal=%s --pid=%p --exec=%e"); + ASSERT_EQ(s_crashes, 0); + ASSERT_NE(logging_.log().find("Enabling crash handling"), std::string::npos); +} + +TEST_F(UserCollectorTest, EnableNoFileAccess) { + collector_.set_core_pattern_file("/does_not_exist"); + ASSERT_FALSE(collector_.Enable()); + ASSERT_EQ(s_crashes, 0); + ASSERT_NE(logging_.log().find("Enabling crash handling"), std::string::npos); + ASSERT_NE(logging_.log().find("Unable to write /does_not_exist"), + std::string::npos); +} + +TEST_F(UserCollectorTest, DisableOK) { + std::string contents; + ASSERT_TRUE(collector_.Disable()); + ASSERT_TRUE(file_util::ReadFileToString(FilePath("test/core_pattern"), + &contents)); + ASSERT_STREQ(contents.c_str(), "core"); + ASSERT_EQ(s_crashes, 0); + ASSERT_NE(logging_.log().find("Disabling crash handling"), + std::string::npos); +} + +TEST_F(UserCollectorTest, DisableNoFileAccess) { + collector_.set_core_pattern_file("/does_not_exist"); + ASSERT_FALSE(collector_.Disable()); + ASSERT_EQ(s_crashes, 0); + ASSERT_NE(logging_.log().find("Disabling crash handling"), std::string::npos); + ASSERT_NE(logging_.log().find("Unable to write /does_not_exist"), + std::string::npos); +} + + +TEST_F(UserCollectorTest, HandleCrashWithoutMetrics) { + s_metrics = false; + collector_.HandleCrash(10, 20, "foobar"); + ASSERT_NE(logging_.log().find( + "Received crash notification for foobar[20] sig 10"), + std::string::npos); + ASSERT_EQ(s_crashes, 0); +} + +TEST_F(UserCollectorTest, HandleCrashWithMetrics) { + s_metrics = true; + collector_.HandleCrash(2, 5, "chrome"); + ASSERT_NE(logging_.log().find( + "Received crash notification for chrome[5] sig 2"), + std::string::npos); + ASSERT_EQ(s_crashes, 1); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 69e37209b3c28dec5fe7ac0bcfcfdec61ef64023 Mon Sep 17 00:00:00 2001 From: Chris Sosa Date: Wed, 16 Jun 2010 17:55:31 -0700 Subject: [PATCH 002/225] TBR: Adding petkov's patch committed today in other repo --- crash_reporter/crash_reporter.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 951d3fae3..e283322da 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -77,6 +77,18 @@ static void CountUserCrash() { s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), CRASH_KIND_USER, CRASH_KIND_MAX); + + // Announce through D-Bus whenever a user crash happens. This is + // used by the metrics daemon to log active use time between + // crashes. + // + // This could be done more efficiently by explicit fork/exec or + // using a dbus library directly. However, this should run + // relatively rarely and longer term we may need to implement a + // better way to do this that doesn't rely on D-Bus. + int status __attribute__((unused)) = + system("/usr/bin/dbus-send --type=signal --system / " + "org.chromium.CrashReporter.UserCrash"); } int main(int argc, char *argv[]) { From 522c061f831325e73e3d46ee2161238ad6fa8b22 Mon Sep 17 00:00:00 2001 From: Chris Sosa Date: Fri, 18 Jun 2010 11:08:11 -0700 Subject: [PATCH 003/225] Change include paths to reflect new directory. TEST=Tested with new ebuild. Review URL: http://codereview.chromium.org/2819009 --- crash_reporter/crash_reporter.cc | 4 ++-- crash_reporter/system_logging.cc | 2 +- crash_reporter/system_logging_mock.h | 2 +- crash_reporter/user_collector.cc | 2 +- crash_reporter/user_collector.h | 2 +- crash_reporter/user_collector_test.cc | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index e283322da..87d4facf5 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -7,8 +7,8 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" -#include "crash/system_logging.h" -#include "crash/user_collector.h" +#include "crash-reporter/system_logging.h" +#include "crash-reporter/user_collector.h" #include "gflags/gflags.h" #include "metrics/metrics_library.h" diff --git a/crash_reporter/system_logging.cc b/crash_reporter/system_logging.cc index e366ef2c2..578951418 100644 --- a/crash_reporter/system_logging.cc +++ b/crash_reporter/system_logging.cc @@ -5,7 +5,7 @@ #include #include -#include "crash/system_logging.h" +#include "crash-reporter/system_logging.h" std::string SystemLoggingImpl::identity_; diff --git a/crash_reporter/system_logging_mock.h b/crash_reporter/system_logging_mock.h index 983c72467..191e53132 100644 --- a/crash_reporter/system_logging_mock.h +++ b/crash_reporter/system_logging_mock.h @@ -7,7 +7,7 @@ #include -#include "crash/system_logging.h" +#include "crash-reporter/system_logging.h" class SystemLoggingMock : public SystemLogging { public: diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 7033ada11..918f6910a 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -7,7 +7,7 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" -#include "crash/user_collector.h" +#include "crash-reporter/user_collector.h" #include "metrics/metrics_library.h" // This procfs file is used to cause kernel core file writing to diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 7f632246a..0d5dd01c4 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -7,7 +7,7 @@ #include -#include "crash/system_logging.h" +#include "crash-reporter/system_logging.h" class FilePath; diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 6398ce707..71cce62f4 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -6,8 +6,8 @@ #include #include "base/file_util.h" -#include "crash/system_logging_mock.h" -#include "crash/user_collector.h" +#include "crash-reporter/system_logging_mock.h" +#include "crash-reporter/user_collector.h" int s_crashes = 0; bool s_metrics = false; From c7f8528cdc7c80e509caca95ca0a33846478222d Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Thu, 24 Jun 2010 11:43:12 -0700 Subject: [PATCH 004/225] Notify metrics daemon of kernel crashes. BUG=none TEST=unit tests, ran crash_reporter --init on the device Review URL: http://codereview.chromium.org/2847017 --- crash_reporter/crash_reporter.cc | 11 +++++++++-- crash_reporter/system_logging_mock.cc | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 87d4facf5..80494cc6c 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -25,6 +25,7 @@ DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; static const char kUncleanShutdownFile[] = "/var/lib/crash_reporter/pending_clean_shutdown"; +static const char kEmpty[] = ""; // Enumeration of kinds of crashes to be used in the CrashCounter histogram. enum CrashKinds { @@ -58,13 +59,19 @@ static void CheckUncleanShutdown() { s_system_log.LogError("Failed to delete unclean shutdown file %s", kUncleanShutdownFile); } + + // Touch a file to notify the metrics daemon that a kernel crash has + // been detected so that it can log the time since the last kernel + // crash. + static const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected"; + FilePath crash_detected(kKernelCrashDetectedFile); + file_util::WriteFile(crash_detected, kEmpty, 0); } static bool PrepareUncleanShutdownCheck() { - static const char empty[] = ""; FilePath file_path(kUncleanShutdownFile); file_util::CreateDirectory(file_path.DirName()); - return file_util::WriteFile(file_path, empty, 0) == 0; + return file_util::WriteFile(file_path, kEmpty, 0) == 0; } static void SignalCleanShutdown() { diff --git a/crash_reporter/system_logging_mock.cc b/crash_reporter/system_logging_mock.cc index 3da195620..a2760e9d5 100644 --- a/crash_reporter/system_logging_mock.cc +++ b/crash_reporter/system_logging_mock.cc @@ -5,7 +5,7 @@ #include #include "base/string_util.h" -#include "crash/system_logging_mock.h" +#include "crash-reporter/system_logging_mock.h" void SystemLoggingMock::LogInfo(const char *format, ...) { va_list vl; From 091993c72bf24087c3f7c2d28124e88dae30a604 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Thu, 1 Jul 2010 15:09:24 -0700 Subject: [PATCH 005/225] Send client ID in crash report BUG=4110,988 Review URL: http://codereview.chromium.org/2849038 --- crash_reporter/Makefile | 23 ++++++-------- crash_reporter/crash_sender | 63 +++++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 31 deletions(-) diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index 1b78f2730..df4dce23f 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -2,29 +2,24 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -CRASH_BIN = crash_reporter -CRASH_LIB = libcrash.so +CRASH_REPORTER = crash_reporter +REPORTER_BINS = $(CRASH_REPORTER) CRASH_OBJS = system_logging.o user_collector.o TEST_OBJS = $(CRASH_OBJS) system_logging_mock.o TEST_BINS = user_collector_test -LIBS = -lbase -lpthread -lgflags -lrt -lmetrics +COMMON_LIBS = -lbase -lpthread -lgflags -lrt +REPORTER_LIBS = $(COMMON_LIBS) -lmetrics TEST_LIBS = $(LIBS) -lgtest -lgmock INCLUDE_DIRS = -I.. -I$(SYSROOT)/usr/include/google-breakpad -LIB_DIRS = -# We need -fPIC for linking objects into shared objects. -CXXFLAGS += -fPIC -Wall -Werror +CXXFLAGS += -Wall -Werror -all: - echo "Specify either $(CRASH_BIN) or $(CRASH_LIB)" +all: $(REPORTER_BINS) -$(CRASH_LIB): crash_dumper.o - $(CXX) -shared -lbreakpad_client $^ -o $@ - -$(CRASH_BIN): crash_reporter.o $(CRASH_OBJS) - $(CXX) $(CXXFLAGS) $(LIB_DIRS) $^ -lcrash $(LIBS) -o $@ +$(CRASH_REPORTER): crash_reporter.o $(CRASH_OBJS) + $(CXX) $(CXXFLAGS) $^ -lcrash $(REPORTER_LIBS) -o $@ tests: $(TEST_BINS) @@ -35,4 +30,4 @@ user_collector_test: user_collector_test.o $(TEST_OBJS) $(CXX) $(CXXFLAGS) $(INCLUDE_DIRS) -c $< -o $@ clean: - rm -rf *.o $(CRASH_BIN) $(TEST_BINS) $(CRASH_LIB) + rm -rf *.o $(CRASH_BIN) $(TEST_BINS) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 4332117ae..90cd661eb 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -9,12 +9,13 @@ set -e # Product ID in crash report CHROMEOS_PRODUCT=ChromeOS +# File whose existence implies crash reports may be sent, and whose +# contents includes our machine's anonymized guid. +CONSENT_ID="/home/chronos/Consent To Send Stats" + # Send up to 8 crashes per day. MAX_CRASH_RATE=8 -# Minidump uploading tool (provided by Google Breakpad). -MINIDUMP_UPLOADER=/usr/bin/minidump_upload - # URL to send non-official build crashes to. MINIDUMP_UPLOAD_STAGING_URL="http://clients2.google.com/cr/staging_report" @@ -32,7 +33,7 @@ PAUSE_CRASH_SENDING="/tmp/pause-crash-sending" RUN_FILE="/var/run/crash_sender.pid" # Maximum time to sleep between sends. -SECONDS_SEND_SPREAD=600 +SECONDS_SEND_SPREAD=${SECONDS_SEND_SPREAD:-600} # The syslog tag for all logging we emit. TAG="$(basename $0)[$$]" @@ -45,8 +46,18 @@ lecho() { logger -t "${TAG}" "$@" } -remove_run_file() { +log_done() { + lecho "Done" +} + +cleanup_tmp_dir() { + rm -rf "${TMP_DIR}" + log_done +} + +cleanup_run_file_and_tmp_dir() { rm -f "${RUN_FILE}" + cleanup_tmp_dir } check_not_already_running() { @@ -55,7 +66,7 @@ check_not_already_running() { fi local last_pid=$(cat "${RUN_FILE}") if [ ! -f "/proc/${last_pid}/cmdline" ]; then - trap remove_run_file EXIT + trap cleanup_run_file_and_tmp_dir EXIT INT echo $$ > "${RUN_FILE}" return fi @@ -81,8 +92,8 @@ generate_uniform_random() { } is_feedback_disabled() { - # See crosbug.com/3303. - return 1 + [ -r "${CONSENT_ID}" ] && return 1 + return 0 } is_on_3g() { @@ -144,16 +155,31 @@ send_crash() { return 1 fi - "${MINIDUMP_UPLOADER}" -p "${CHROMEOS_PRODUCT}" \ - -v "${chromeos_version}" "${minidump_path}" "${url}" - return $? + local report_id="${TMP_DIR}/report_id" + local curl_stderr="${TMP_DIR}/curl_stderr" + + set +e + curl "${url}" \ + -F "prod=${CHROMEOS_PRODUCT}" \ + -F "ver=${chromeos_version}" \ + -F "upload_file_minidump=@${minidump_path}" \ + -F "guid=<${CONSENT_ID}" -o "${report_id}" 2>"${curl_stderr}" + local curl_result=$? + set -e + + if [ ${curl_result} -eq 0 ]; then + lecho "Crash report receipt ID $(cat ${report_id})" + else + lecho "Crash sending failed with: $(cat ${curl_stderr})" + fi + + rm -f "${report_id}" "${output_file}" + + return ${curl_result} } # Send all crashes from the given directory. The directory is currently -# expected to just contain a bunch of minidumps - but this will change -# over time to be a directory of directories where the minidump and core -# file are in the directory as well as other metadata about the context -# of the crash (executable name for instance). +# expected to just contain a bunch of minidumps. send_crashes() { local dir="$1" lecho "Considering crashes in ${dir}" @@ -187,6 +213,8 @@ send_crashes() { main() { lecho "Starting" + trap log_done EXIT INT + if [ -e "${PAUSE_CRASH_SENDING}" ]; then lecho "Exiting early due to ${PAUSE_CRASH_SENDING}" exit 1 @@ -194,13 +222,14 @@ main() { check_not_already_running + TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXX)" + trap cleanup_tmp_dir EXIT INT + # Send system-wide crashes send_crashes "/var/spool/crash" # Send user-specific crashes send_crashes "/home/chronos/user/crash" - - lecho "Done" } main From 6993256118855efed28ea74044439867b8c0a19a Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Fri, 9 Jul 2010 12:37:59 -0700 Subject: [PATCH 006/225] Fix crash-reporter test build Review URL: http://codereview.chromium.org/2944003 --- crash_reporter/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index df4dce23f..ccd39be96 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -11,7 +11,7 @@ TEST_BINS = user_collector_test COMMON_LIBS = -lbase -lpthread -lgflags -lrt REPORTER_LIBS = $(COMMON_LIBS) -lmetrics -TEST_LIBS = $(LIBS) -lgtest -lgmock +TEST_LIBS = $(COMMON_LIBS) -lgtest -lgmock INCLUDE_DIRS = -I.. -I$(SYSROOT)/usr/include/google-breakpad CXXFLAGS += -Wall -Werror From 777484c41cc63e2f97f41a443d1d55f7e6037a5e Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Fri, 23 Jul 2010 16:22:44 -0700 Subject: [PATCH 007/225] Start invoking core2md to implement full system crash reporting BUG=4741 Review URL: http://codereview.chromium.org/3040013 --- crash_reporter/Makefile | 2 +- crash_reporter/crash_reporter.cc | 27 +- crash_reporter/crash_sender | 23 +- crash_reporter/system_logging.h | 6 +- crash_reporter/system_logging_mock.h | 6 +- crash_reporter/user_collector.cc | 357 +++++++++++++++++++++++++- crash_reporter/user_collector.h | 77 +++++- crash_reporter/user_collector_test.cc | 227 +++++++++++++++- 8 files changed, 676 insertions(+), 49 deletions(-) diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index ccd39be96..ae72a6d5b 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -19,7 +19,7 @@ CXXFLAGS += -Wall -Werror all: $(REPORTER_BINS) $(CRASH_REPORTER): crash_reporter.o $(CRASH_OBJS) - $(CXX) $(CXXFLAGS) $^ -lcrash $(REPORTER_LIBS) -o $@ + $(CXX) $(CXXFLAGS) $^ $(REPORTER_LIBS) -o $@ tests: $(TEST_BINS) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 80494cc6c..0930cea44 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -16,22 +16,22 @@ DEFINE_bool(init, false, "Initialize crash logging"); DEFINE_bool(clean_shutdown, false, "Signal clean shutdown"); DEFINE_bool(crash_test, false, "Crash test"); -DEFINE_string(exec, "", "Executable name crashed"); DEFINE_int32(pid, -1, "Crashing PID"); DEFINE_int32(signal, -1, "Signal causing crash"); DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); #pragma GCC diagnostic error "-Wstrict-aliasing" static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; +static const char kEmpty[] = ""; static const char kUncleanShutdownFile[] = "/var/lib/crash_reporter/pending_clean_shutdown"; -static const char kEmpty[] = ""; + // Enumeration of kinds of crashes to be used in the CrashCounter histogram. enum CrashKinds { - CRASH_KIND_KERNEL = 1, - CRASH_KIND_USER = 2, - CRASH_KIND_MAX + kCrashKindKernel = 1, + kCrashKindUser = 2, + kCrashKindMax }; static MetricsLibrary s_metrics_lib; @@ -52,8 +52,8 @@ static void CheckUncleanShutdown() { s_system_log.LogWarning("Last shutdown was not clean"); if (IsMetricsCollectionAllowed()) { s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), - CRASH_KIND_KERNEL, - CRASH_KIND_MAX); + kCrashKindKernel, + kCrashKindMax); } if (!file_util::Delete(unclean_file_path, false)) { s_system_log.LogError("Failed to delete unclean shutdown file %s", @@ -82,8 +82,8 @@ static void SignalCleanShutdown() { static void CountUserCrash() { CHECK(IsMetricsCollectionAllowed()); s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), - CRASH_KIND_USER, - CRASH_KIND_MAX); + kCrashKindUser, + kCrashKindMax); // Announce through D-Bus whenever a user crash happens. This is // used by the metrics daemon to log active use time between @@ -108,7 +108,8 @@ int main(int argc, char *argv[]) { user_collector.Initialize(CountUserCrash, my_path.value(), IsMetricsCollectionAllowed, - &s_system_log); + &s_system_log, + true); // generate_diagnostics if (FLAGS_init) { CHECK(!FLAGS_clean_shutdown) << "Incompatible options"; @@ -131,7 +132,6 @@ int main(int argc, char *argv[]) { // Handle a specific user space crash. CHECK(FLAGS_signal != -1) << "Signal must be set"; CHECK(FLAGS_pid != -1) << "PID must be set"; - CHECK(FLAGS_exec != "") << "Executable name must be set"; // Make it possible to test what happens when we crash while // handling a crash. @@ -140,7 +140,10 @@ int main(int argc, char *argv[]) { return 0; } - user_collector.HandleCrash(FLAGS_signal, FLAGS_pid, FLAGS_exec); + // Handle the crash, get the name of the process from procfs. + if (!user_collector.HandleCrash(FLAGS_signal, FLAGS_pid, NULL)) { + return 1; + } return 0; } diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 90cd661eb..6da893354 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -46,13 +46,8 @@ lecho() { logger -t "${TAG}" "$@" } -log_done() { - lecho "Done" -} - cleanup_tmp_dir() { rm -rf "${TMP_DIR}" - log_done } cleanup_run_file_and_tmp_dir() { @@ -119,6 +114,12 @@ check_rate() { return 0 } +# Return if $1 is a .core file +is_core_file() { + local filename=$1 + [ "${filename##*.}" = "core" ] +} + send_crash() { local sleep_time=$(generate_uniform_random $SECONDS_SEND_SPREAD) local url="${MINIDUMP_UPLOAD_STAGING_URL}" @@ -183,6 +184,7 @@ send_crash() { send_crashes() { local dir="$1" lecho "Considering crashes in ${dir}" + # Cycle through minidumps, most recent first. That way if we're about # to exceed the daily rate, we send the most recent minidumps. if [ ! -d "${dir}" ]; then @@ -190,11 +192,15 @@ send_crashes() { fi for file in $(ls -1t "${dir}"); do local minidump_path="${dir}/${file}" - lecho "Considering crash ${minidump_path}" + lecho "Considering file ${minidump_path}" + if is_core_file "${minidump_path}"; then + lecho "Ignoring core file." + continue + fi if ! check_rate; then lecho "Sending ${minidump_path} would exceed rate. Leaving for later." return 0 - fi + fi local chromeos_version=$(get_version) if is_feedback_disabled; then lecho "Uploading is disabled. Removing crash." @@ -212,9 +218,6 @@ send_crashes() { } main() { - lecho "Starting" - trap log_done EXIT INT - if [ -e "${PAUSE_CRASH_SENDING}" ]; then lecho "Exiting early due to ${PAUSE_CRASH_SENDING}" exit 1 diff --git a/crash_reporter/system_logging.h b/crash_reporter/system_logging.h index 8c946b01a..7ebfc5e59 100644 --- a/crash_reporter/system_logging.h +++ b/crash_reporter/system_logging.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CRASH_SYSTEM_LOGGING_H_ -#define CRASH_SYSTEM_LOGGING_H_ +#ifndef CRASH_REPORTER_SYSTEM_LOGGING_H_ +#define CRASH_REPORTER_SYSTEM_LOGGING_H_ #include @@ -27,4 +27,4 @@ class SystemLoggingImpl : public SystemLogging { static std::string identity_; }; -#endif // CRASH_SYSTEM_LOGGING_H_ +#endif // CRASH_REPORTER_SYSTEM_LOGGING_H_ diff --git a/crash_reporter/system_logging_mock.h b/crash_reporter/system_logging_mock.h index 191e53132..639fe4257 100644 --- a/crash_reporter/system_logging_mock.h +++ b/crash_reporter/system_logging_mock.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CRASH_SYSTEM_LOGGING_MOCK_H_ -#define CRASH_SYSTEM_LOGGING_MOCK_H_ +#ifndef CRASH_REPORTER_SYSTEM_LOGGING_MOCK_H_ +#define CRASH_REPORTER_SYSTEM_LOGGING_MOCK_H_ #include @@ -24,4 +24,4 @@ class SystemLoggingMock : public SystemLogging { std::string ident_; }; -#endif // CRASH_SYSTEM_LOGGING_H_ +#endif // CRASH_REPORTER_SYSTEM_LOGGING_H_ diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 918f6910a..7af35ce33 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include // For struct group. +#include // For struct passwd. +#include // For getpwuid_r and getgrnam_r. + #include #include "base/file_util.h" @@ -14,9 +18,27 @@ // instead pipe the core file into a user space process. See // core(5) man page. static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern"; +static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; +static const char kDefaultUserName[] = "chronos"; +static const char kLeaveCoreFile[] = "/etc/leave_core"; +static const char kSystemCrashPath[] = "/var/spool/crash"; +static const char kUserCrashPath[] = "/home/chronos/user/crash"; + +// Directory mode of the user crash spool directory. +static const mode_t kUserCrashPathMode = 0755; + +// Directory mode of the system crash spool directory. +static const mode_t kSystemCrashPathMode = 01755; + +static const uid_t kRootOwner = 0; +static const uid_t kRootGroup = 0; + +const char *UserCollector::kUserId = "Uid:\t"; +const char *UserCollector::kGroupId = "Gid:\t"; UserCollector::UserCollector() - : core_pattern_file_(kCorePatternFile), + : generate_diagnostics_(false), + core_pattern_file_(kCorePatternFile), count_crash_function_(NULL), initialized_(false), is_feedback_allowed_function_(NULL), @@ -27,7 +49,8 @@ void UserCollector::Initialize( UserCollector::CountCrashFunction count_crash_function, const std::string &our_path, UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function, - SystemLogging *logger) { + SystemLogging *logger, + bool generate_diagnostics) { CHECK(count_crash_function != NULL); CHECK(is_feedback_allowed_function != NULL); CHECK(logger != NULL); @@ -37,6 +60,7 @@ void UserCollector::Initialize( is_feedback_allowed_function_ = is_feedback_allowed_function; logger_ = logger; initialized_ = true; + generate_diagnostics_ = generate_diagnostics; } UserCollector::~UserCollector() { @@ -44,8 +68,7 @@ UserCollector::~UserCollector() { std::string UserCollector::GetPattern(bool enabled) const { if (enabled) { - return StringPrintf("|%s --signal=%%s --pid=%%p --exec=%%e", - our_path_.c_str()); + return StringPrintf("|%s --signal=%%s --pid=%%p", our_path_.c_str()); } else { return "core"; } @@ -65,12 +88,336 @@ bool UserCollector::SetUpInternal(bool enabled) { return true; } -void UserCollector::HandleCrash(int signal, int pid, const std::string &exec) { +FilePath UserCollector::GetProcessPath(pid_t pid) { + return FilePath(StringPrintf("/proc/%d", pid)); +} + +bool UserCollector::GetSymlinkTarget(const FilePath &symlink, + FilePath *target) { + int max_size = 32; + scoped_array buffer; + while (true) { + buffer.reset(new char[max_size + 1]); + ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size); + if (size < 0) { + return false; + } + buffer[size] = 0; + if (size == max_size) { + // Avoid overflow when doubling. + if (max_size * 2 > max_size) { + max_size *= 2; + continue; + } else { + return false; + } + } + break; + } + + *target = FilePath(buffer.get()); + return true; +} + +bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid, + std::string *base_name) { + FilePath target; + if (!GetSymlinkTarget(GetProcessPath(pid).Append("exe"), &target)) + return false; + *base_name = target.BaseName().value(); + return true; +} + +bool UserCollector::GetIdFromStatus(const char *prefix, + IdKind kind, + const std::string &status_contents, + int *id) { + // From fs/proc/array.c:task_state(), this file contains: + // \nUid:\t\t\t\t\n + std::vector status_lines; + SplitString(status_contents, '\n', &status_lines); + std::vector::iterator line_iterator; + for (line_iterator = status_lines.begin(); + line_iterator != status_lines.end(); + ++line_iterator) { + if (line_iterator->find(prefix) == 0) + break; + } + if (line_iterator == status_lines.end()) { + return false; + } + std::string id_substring = line_iterator->substr(strlen(prefix), + std::string::npos); + std::vector ids; + SplitString(id_substring, '\t', &ids); + if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) { + return false; + } + const char *number = ids[kind].c_str(); + char *end_number = NULL; + *id = strtol(number, &end_number, 10); + if (*end_number != '\0') + return false; + return true; +} + +bool UserCollector::GetUserInfoFromName(const std::string &name, + uid_t *uid, + gid_t *gid) { + char storage[256]; + struct passwd passwd_storage; + struct passwd *passwd_result = NULL; + + if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage), + &passwd_result) != 0 || passwd_result == NULL) { + logger_->LogError("Cannot find user named %s", name.c_str()); + return false; + } + + *uid = passwd_result->pw_uid; + *gid = passwd_result->pw_gid; + return true; +} + +bool UserCollector::CopyOffProcFiles(pid_t pid, + const FilePath &container_dir) { + if (!file_util::CreateDirectory(container_dir)) { + logger_->LogInfo("Could not create %s", container_dir.value().c_str()); + return false; + } + FilePath process_path = GetProcessPath(pid); + if (!file_util::PathExists(process_path)) { + logger_->LogWarning("Path %s does not exist", + process_path.value().c_str()); + return false; + } + static const char *proc_files[] = { + "auxv", + "cmdline", + "environ", + "maps", + "status" + }; + for (unsigned i = 0; i < arraysize(proc_files); ++i) { + if (!file_util::CopyFile(process_path.Append(proc_files[i]), + container_dir.Append(proc_files[i]))) { + logger_->LogWarning("Could not copy %s file", proc_files[i]); + return false; + } + } + return true; +} + +FilePath UserCollector::GetCrashDirectoryInfo( + uid_t process_euid, + uid_t default_user_id, + gid_t default_user_group, + mode_t *mode, + uid_t *directory_owner, + gid_t *directory_group) { + if (process_euid == default_user_id) { + *mode = kUserCrashPathMode; + *directory_owner = default_user_id; + *directory_group = default_user_group; + return FilePath(kUserCrashPath); + } else { + *mode = kSystemCrashPathMode; + *directory_owner = kRootOwner; + *directory_group = kRootGroup; + return FilePath(kSystemCrashPath); + } +} + +bool UserCollector::GetCreatedCrashDirectory(pid_t pid, + FilePath *crash_file_path) { + FilePath process_path = GetProcessPath(pid); + std::string status; + if (!file_util::ReadFileToString(process_path.Append("status"), + &status)) { + logger_->LogError("Could not read status file"); + return false; + } + int process_euid; + if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { + logger_->LogError("Could not find euid in status file"); + return false; + } + uid_t default_user_id; + gid_t default_user_group; + if (!GetUserInfoFromName(kDefaultUserName, + &default_user_id, + &default_user_group)) { + logger_->LogError("Could not find default user info"); + return false; + } + mode_t directory_mode; + uid_t directory_owner; + gid_t directory_group; + *crash_file_path = + GetCrashDirectoryInfo(process_euid, + default_user_id, + default_user_group, + &directory_mode, + &directory_owner, + &directory_group); + + + if (!file_util::PathExists(*crash_file_path)) { + // Create the spool directory with the appropriate mode (regardless of + // umask) and ownership. + mode_t old_mask = umask(0); + if (mkdir(crash_file_path->value().c_str(), directory_mode) < 0 || + chown(crash_file_path->value().c_str(), + directory_owner, + directory_group) < 0) { + logger_->LogError("Unable to create appropriate crash directory"); + return false; + } + umask(old_mask); + } + + if (!file_util::PathExists(*crash_file_path)) { + logger_->LogError("Unable to create crash directory %s", + crash_file_path->value().c_str()); + return false; + } + + + return true; +} + +std::string UserCollector::FormatDumpBasename(const std::string &exec_name, + time_t timestamp, + pid_t pid) { + struct tm tm; + localtime_r(×tamp, &tm); + return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d", + exec_name.c_str(), + tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + pid); +} + +bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { + // Copy off all stdin to a core file. + FilePath stdin_path("/dev/fd/0"); + if (file_util::CopyFile(stdin_path, core_path)) { + return true; + } + + logger_->LogError("Could not write core file"); + // If the file system was full, make sure we remove any remnants. + file_util::Delete(core_path, false); + return false; +} + +bool UserCollector::ConvertCoreToMinidump(const FilePath &core_path, + const FilePath &procfs_directory, + const FilePath &minidump_path, + const FilePath &temp_directory) { + // TODO(kmixter): Rewrite to use process_util once it's included in + // libchrome. + FilePath output_path = temp_directory.Append("output"); + std::string core2md_command = + StringPrintf("\"%s\" \"%s\" \"%s\" \"%s\" > \"%s\" 2>&1", + kCoreToMinidumpConverterPath, + core_path.value().c_str(), + procfs_directory.value().c_str(), + minidump_path.value().c_str(), + output_path.value().c_str()); + int errorlevel = system(core2md_command.c_str()); + + std::string output; + file_util::ReadFileToString(output_path, &output); + if (errorlevel != 0) { + logger_->LogInfo("Problem during %s [result=%d]: %s", + core2md_command.c_str(), + errorlevel, + output.c_str()); + return false; + } + + if (!file_util::PathExists(minidump_path)) { + logger_->LogError("Minidump file %s was not created", + minidump_path.value().c_str()); + return false; + } + return true; +} + +bool UserCollector::GenerateDiagnostics(pid_t pid, + const std::string &exec_name) { + FilePath container_dir("/tmp"); + container_dir = container_dir.Append( + StringPrintf("crash_reporter.%d", pid)); + + if (!CopyOffProcFiles(pid, container_dir)) { + file_util::Delete(container_dir, true); + return false; + } + + FilePath spool_path; + if (!GetCreatedCrashDirectory(pid, &spool_path)) { + file_util::Delete(container_dir, true); + return false; + } + std::string dump_basename = FormatDumpBasename(exec_name, time(NULL), pid); + FilePath core_path = spool_path.Append( + StringPrintf("%s.core", dump_basename.c_str())); + + if (!CopyStdinToCoreFile(core_path)) { + file_util::Delete(container_dir, true); + return false; + } + + FilePath minidump_path = spool_path.Append( + StringPrintf("%s.dmp", dump_basename.c_str())); + + bool conversion_result = true; + if (!ConvertCoreToMinidump(core_path, + container_dir, // procfs directory + minidump_path, + container_dir)) { // temporary directory + // Note we leave the container directory for inspection. + conversion_result = false; + } + + if (conversion_result) { + logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str()); + } + + if (!file_util::PathExists(FilePath(kLeaveCoreFile))) { + file_util::Delete(core_path, false); + } else { + logger_->LogInfo("Leaving core file at %s", core_path.value().c_str()); + } + + return conversion_result; +} + +bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { CHECK(initialized_); + std::string exec; + if (force_exec) { + exec.assign(force_exec); + } else if (!GetExecutableBaseNameFromPid(pid, &exec)) { + // If for some reason we don't have the base name, avoid completely + // failing by indicating an unknown name. + exec = "unknown"; + } logger_->LogWarning("Received crash notification for %s[%d] sig %d", exec.c_str(), pid, signal); if (is_feedback_allowed_function_()) { count_crash_function_(); } + + if (generate_diagnostics_) { + return GenerateDiagnostics(pid, exec); + } + return true; } diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 0d5dd01c4..dc633bba4 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef _CRASH_USER_COLLECTOR_H_ -#define _CRASH_USER_COLLECTOR_H_ +#ifndef _CRASH_REPORTER_USER_COLLECTOR_H_ +#define _CRASH_REPORTER_USER_COLLECTOR_H_ #include #include "crash-reporter/system_logging.h" +#include "gtest/gtest_prod.h" // for FRIEND_TEST class FilePath; @@ -22,12 +23,14 @@ class UserCollector { // Initialize the user crash collector for detection of crashes, // given a crash counting function, the path to this executable, // metrics collection enabled oracle, and system logger facility. - // Crash detection/reporting is not enabled until Enable is - // called. + // Crash detection/reporting is not enabled until Enable is called. + // |generate_diagnostics| is used to indicate whether or not to try + // to generate a minidump from crashes. void Initialize(CountCrashFunction count_crash, const std::string &our_path, IsFeedbackAllowedFunction is_metrics_allowed, - SystemLogging *logger); + SystemLogging *logger, + bool generate_diagnostics); virtual ~UserCollector(); @@ -37,8 +40,8 @@ class UserCollector { // Disable collection. bool Disable() { return SetUpInternal(false); } - // Handle a specific user crash. - void HandleCrash(int signal, int pid, const std::string &exec); + // Handle a specific user crash. Returns true on success. + bool HandleCrash(int signal, int pid, const char *force_exec); // Set (override the default) core file pattern. void set_core_pattern_file(const std::string &pattern) { @@ -47,16 +50,74 @@ class UserCollector { private: friend class UserCollectorTest; + FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPath); + FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPid); + FRIEND_TEST(UserCollectorTest, CopyOffProcFilesOK); + FRIEND_TEST(UserCollectorTest, FormatDumpBasename); + FRIEND_TEST(UserCollectorTest, GetCrashDirectoryInfo); + FRIEND_TEST(UserCollectorTest, GetIdFromStatus); + FRIEND_TEST(UserCollectorTest, GetProcessPath); + FRIEND_TEST(UserCollectorTest, GetSymlinkTarget); + FRIEND_TEST(UserCollectorTest, GetUserInfoFromName); + + // Enumeration to pass to GetIdFromStatus. Must match the order + // that the kernel lists IDs in the status file. + enum IdKind { + kIdReal = 0, // uid and gid + kIdEffective = 1, // euid and egid + kIdSet = 2, // suid and sgid + kIdFileSystem = 3, // fsuid and fsgid + kIdMax + }; std::string GetPattern(bool enabled) const; bool SetUpInternal(bool enabled); + FilePath GetProcessPath(pid_t pid); + bool GetSymlinkTarget(const FilePath &symlink, + FilePath *target); + bool GetExecutableBaseNameFromPid(uid_t pid, + std::string *base_name); + bool GetIdFromStatus(const char *prefix, + IdKind kind, + const std::string &status_contents, + int *id); + bool GetUserInfoFromName(const std::string &name, + uid_t *uid, + gid_t *gid); + bool CopyOffProcFiles(pid_t pid, const FilePath &process_map); + FilePath GetCrashDirectoryInfo(uid_t process_euid, + uid_t default_user_id, + gid_t default_user_group, + mode_t *mode, + uid_t *directory_owner, + gid_t *directory_group); + // Determines the crash directory for given pid based on pid's owner, + // and creates the directory if necessary with appropriate permissions. + // Returns true whether or not directory needed to be created, false on + // any failure. + bool GetCreatedCrashDirectory(pid_t pid, + FilePath *crash_file_path); + std::string FormatDumpBasename(const std::string &exec_name, + time_t timestamp, + pid_t pid); + bool CopyStdinToCoreFile(const FilePath &core_path); + bool ConvertCoreToMinidump(const FilePath &core_path, + const FilePath &procfs_directory, + const FilePath &minidump_path, + const FilePath &temp_directory); + bool GenerateDiagnostics(pid_t pid, const std::string &exec_name); + + bool generate_diagnostics_; std::string core_pattern_file_; CountCrashFunction count_crash_function_; std::string our_path_; bool initialized_; IsFeedbackAllowedFunction is_feedback_allowed_function_; SystemLogging *logger_; + + static const char *kUserId; + static const char *kGroupId; }; -#endif // _CRASH_USER_COLLECTOR_H_ +#endif // _CRASH_REPORTER_USER_COLLECTOR_H_ diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 71cce62f4..335821c74 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include -#include +#include #include "base/file_util.h" #include "crash-reporter/system_logging_mock.h" #include "crash-reporter/user_collector.h" +#include "gflags/gflags.h" +#include "gtest/gtest.h" int s_crashes = 0; bool s_metrics = false; @@ -28,13 +29,18 @@ class UserCollectorTest : public ::testing::Test { collector_.Initialize(CountCrash, kFilePath, IsMetrics, - &logging_); + &logging_, + false); mkdir("test", 0777); collector_.set_core_pattern_file("test/core_pattern"); + pid_ = getpid(); } protected: + void TestEnableOK(bool generate_diagnostics); + SystemLoggingMock logging_; UserCollector collector_; + pid_t pid_; }; TEST_F(UserCollectorTest, EnableOK) { @@ -42,8 +48,7 @@ TEST_F(UserCollectorTest, EnableOK) { ASSERT_TRUE(collector_.Enable()); ASSERT_TRUE(file_util::ReadFileToString(FilePath("test/core_pattern"), &contents)); - ASSERT_STREQ(contents.c_str(), - "|/my/path --signal=%s --pid=%p --exec=%e"); + ASSERT_EQ("|/my/path --signal=%s --pid=%p", contents); ASSERT_EQ(s_crashes, 0); ASSERT_NE(logging_.log().find("Enabling crash handling"), std::string::npos); } @@ -62,7 +67,7 @@ TEST_F(UserCollectorTest, DisableOK) { ASSERT_TRUE(collector_.Disable()); ASSERT_TRUE(file_util::ReadFileToString(FilePath("test/core_pattern"), &contents)); - ASSERT_STREQ(contents.c_str(), "core"); + ASSERT_EQ("core", contents); ASSERT_EQ(s_crashes, 0); ASSERT_NE(logging_.log().find("Disabling crash handling"), std::string::npos); @@ -77,7 +82,6 @@ TEST_F(UserCollectorTest, DisableNoFileAccess) { std::string::npos); } - TEST_F(UserCollectorTest, HandleCrashWithoutMetrics) { s_metrics = false; collector_.HandleCrash(10, 20, "foobar"); @@ -96,6 +100,215 @@ TEST_F(UserCollectorTest, HandleCrashWithMetrics) { ASSERT_EQ(s_crashes, 1); } +TEST_F(UserCollectorTest, GetProcessPath) { + FilePath path = collector_.GetProcessPath(100); + ASSERT_EQ("/proc/100", path.value()); +} + +TEST_F(UserCollectorTest, GetSymlinkTarget) { + FilePath result; + ASSERT_FALSE(collector_.GetSymlinkTarget(FilePath("/does_not_exist"), + &result)); + + std::string long_link; + for (int i = 0; i < 50; ++i) + long_link += "0123456789"; + long_link += "/gold"; + + for (size_t len = 1; len <= long_link.size(); ++len) { + std::string this_link; + static const char kLink[] = "test/this_link"; + this_link.assign(long_link.c_str(), len); + ASSERT_EQ(len, this_link.size()); + unlink(kLink); + ASSERT_EQ(0, symlink(this_link.c_str(), kLink)); + ASSERT_TRUE(collector_.GetSymlinkTarget(FilePath(kLink), &result)); + ASSERT_EQ(this_link, result.value()); + } +} + +TEST_F(UserCollectorTest, GetIdFromStatus) { + int id = 1; + EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId, + UserCollector::kIdEffective, + "nothing here", + &id)); + EXPECT_EQ(id, 1); + + // Not enough parameters. + EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId, + UserCollector::kIdReal, + "line 1\nUid:\t1\n", &id)); + + const char valid_contents[] = "\nUid:\t1\t2\t3\t4\nGid:\t5\t6\t7\t8\n"; + EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId, + UserCollector::kIdReal, + valid_contents, + &id)); + EXPECT_EQ(1, id); + + EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId, + UserCollector::kIdEffective, + valid_contents, + &id)); + EXPECT_EQ(2, id); + + EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId, + UserCollector::kIdFileSystem, + valid_contents, + &id)); + EXPECT_EQ(4, id); + + EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kGroupId, + UserCollector::kIdEffective, + valid_contents, + &id)); + EXPECT_EQ(6, id); + + EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kGroupId, + UserCollector::kIdSet, + valid_contents, + &id)); + EXPECT_EQ(7, id); + + EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kGroupId, + UserCollector::IdKind(5), + valid_contents, + &id)); + EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kGroupId, + UserCollector::IdKind(-1), + valid_contents, + &id)); + + // Fail if junk after number + EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId, + UserCollector::kIdReal, + "Uid:\t1f\t2\t3\t4\n", + &id)); + EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId, + UserCollector::kIdReal, + "Uid:\t1\t2\t3\t4\n", + &id)); + EXPECT_EQ(1, id); + + // Fail if more than 4 numbers. + EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId, + UserCollector::kIdReal, + "Uid:\t1\t2\t3\t4\t5\n", + &id)); +} + +TEST_F(UserCollectorTest, GetUserInfoFromName) { + gid_t gid = 100; + uid_t uid = 100; + EXPECT_TRUE(collector_.GetUserInfoFromName("root", &uid, &gid)); + EXPECT_EQ(0, uid); + EXPECT_EQ(0, gid); +} + +TEST_F(UserCollectorTest, GetCrashDirectoryInfo) { + FilePath path; + const int kRootUid = 0; + const int kRootGid = 0; + const int kNtpUid = 5; + const int kChronosUid = 1000; + const int kChronosGid = 1001; + const mode_t kExpectedSystemMode = 01755; + const mode_t kExpectedUserMode = 0755; + + mode_t directory_mode; + uid_t directory_owner; + gid_t directory_group; + + path = collector_.GetCrashDirectoryInfo(kRootUid, + kChronosUid, + kChronosGid, + &directory_mode, + &directory_owner, + &directory_group); + EXPECT_EQ("/var/spool/crash", path.value()); + EXPECT_EQ(kExpectedSystemMode, directory_mode); + EXPECT_EQ(kRootUid, directory_owner); + EXPECT_EQ(kRootGid, directory_group); + + path = collector_.GetCrashDirectoryInfo(kNtpUid, + kChronosUid, + kChronosGid, + &directory_mode, + &directory_owner, + &directory_group); + EXPECT_EQ("/var/spool/crash", path.value()); + EXPECT_EQ(kExpectedSystemMode, directory_mode); + EXPECT_EQ(kRootUid, directory_owner); + EXPECT_EQ(kRootGid, directory_group); + + path = collector_.GetCrashDirectoryInfo(kChronosUid, + kChronosUid, + kChronosGid, + &directory_mode, + &directory_owner, + &directory_group); + EXPECT_EQ("/home/chronos/user/crash", path.value()); + EXPECT_EQ(kExpectedUserMode, directory_mode); + EXPECT_EQ(kChronosUid, directory_owner); + EXPECT_EQ(kChronosGid, directory_group); +} + +TEST_F(UserCollectorTest, CopyOffProcFilesBadPath) { + // Try a path that is not writable. + ASSERT_FALSE(collector_.CopyOffProcFiles(pid_, FilePath("/bad/path"))); + ASSERT_NE(logging_.log().find( + "Could not create /bad/path"), + std::string::npos); +} + +TEST_F(UserCollectorTest, CopyOffProcFilesBadPid) { + FilePath container_path("test/container"); + ASSERT_FALSE(collector_.CopyOffProcFiles(0, container_path)); + ASSERT_NE(logging_.log().find( + "Path /proc/0 does not exist"), + std::string::npos); +} + +TEST_F(UserCollectorTest, CopyOffProcFilesOK) { + FilePath container_path("test/container"); + ASSERT_TRUE(collector_.CopyOffProcFiles(pid_, container_path)); + ASSERT_EQ(logging_.log().find( + "Could not copy"), std::string::npos); + static struct { + const char *name; + bool exists; + } expectations[] = { + { "auxv", true }, + { "cmdline", true }, + { "environ", true }, + { "maps", true }, + { "mem", false }, + { "mounts", false }, + { "sched", false }, + { "status", true } + }; + for (unsigned i = 0; i < sizeof(expectations)/sizeof(expectations[0]); ++i) { + EXPECT_EQ(expectations[i].exists, + file_util::PathExists( + container_path.Append(expectations[i].name))); + } +} + +TEST_F(UserCollectorTest, FormatDumpBasename) { + struct tm tm = {0}; + tm.tm_sec = 15; + tm.tm_min = 50; + tm.tm_hour = 13; + tm.tm_mday = 23; + tm.tm_mon = 4; + tm.tm_year = 110; + tm.tm_isdst = -1; + std::string basename = + collector_.FormatDumpBasename("foo", mktime(&tm), 100); + ASSERT_EQ("foo.20100523.135015.100", basename); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From 0e6037cfc1cf968f80a24de5a6794f88ad047a28 Mon Sep 17 00:00:00 2001 From: "J. Richard Barnette" Date: Thu, 5 Aug 2010 14:13:48 -0700 Subject: [PATCH 008/225] Add LICENSE file --- crash_reporter/LICENSE | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 crash_reporter/LICENSE diff --git a/crash_reporter/LICENSE b/crash_reporter/LICENSE new file mode 100644 index 000000000..d25149653 --- /dev/null +++ b/crash_reporter/LICENSE @@ -0,0 +1,27 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From f9322f734ff8ce1d9a09901819b0a221ac94bd21 Mon Sep 17 00:00:00 2001 From: Chris Masone Date: Wed, 11 Aug 2010 19:56:51 -0700 Subject: [PATCH 009/225] Update to new libchrome APIs BUG=none TEST=compile Change-Id: Ie494a6536665b635b82725dd6a82959974b02a75 Review URL: http://codereview.chromium.org/3138010 --- crash_reporter/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index ae72a6d5b..b51743d35 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -8,7 +8,8 @@ CRASH_OBJS = system_logging.o user_collector.o TEST_OBJS = $(CRASH_OBJS) system_logging_mock.o TEST_BINS = user_collector_test -COMMON_LIBS = -lbase -lpthread -lgflags -lrt +# -lglib-2.0 is needed by libbase.a now. +COMMON_LIBS = -lbase -lpthread -lglib-2.0 -lgflags -lrt REPORTER_LIBS = $(COMMON_LIBS) -lmetrics TEST_LIBS = $(COMMON_LIBS) -lgtest -lgmock From 7ac7a70a592a95624cdf01393474d786173e7bf8 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Fri, 13 Aug 2010 15:43:34 -0700 Subject: [PATCH 010/225] Move leave_core where similar files exist BUG=5696 Review URL: http://codereview.chromium.org/3121015 --- crash_reporter/user_collector.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 7af35ce33..18ff39a09 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -20,7 +20,7 @@ static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern"; static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; static const char kDefaultUserName[] = "chronos"; -static const char kLeaveCoreFile[] = "/etc/leave_core"; +static const char kLeaveCoreFile[] = "/root/.leave_core"; static const char kSystemCrashPath[] = "/var/spool/crash"; static const char kUserCrashPath[] = "/home/chronos/user/crash"; From 479b148ba7ea0e8d716558cf49dd94174087388e Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Wed, 18 Aug 2010 14:02:01 -0700 Subject: [PATCH 011/225] Hard fail immediately in crash_sender if /usr/bin/find is not installed BUGS=5778 TEST=BVTs Review URL: http://codereview.chromium.org/3122024 --- crash_reporter/crash_sender | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 6da893354..99cc0def3 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -13,6 +13,9 @@ CHROMEOS_PRODUCT=ChromeOS # contents includes our machine's anonymized guid. CONSENT_ID="/home/chronos/Consent To Send Stats" +# Path to find which is required for computing the crash rate. +FIND="/usr/bin/find" + # Send up to 8 crashes per day. MAX_CRASH_RATE=8 @@ -101,7 +104,7 @@ is_on_3g() { check_rate() { mkdir -p ${TIMESTAMPS_DIR} # Only consider minidumps written in the past 24 hours by removing all older. - find "${TIMESTAMPS_DIR}" -mindepth 1 -mmin +$((24 * 60)) -exec rm '{}' ';' + ${FIND} "${TIMESTAMPS_DIR}" -mindepth 1 -mmin +$((24 * 60)) -exec rm '{}' ';' local sends_in_24hrs=$(echo "${TIMESTAMPS_DIR}"/* | wc -w) lecho "Current send rate: ${sends_in_24hrs}sends/24hrs" if [ ${sends_in_24hrs} -ge ${MAX_CRASH_RATE} ]; then @@ -225,6 +228,11 @@ main() { check_not_already_running + if [ ! -x "${FIND}" ]; then + lecho "Fatal: Crash sending disabled: ${FIND} not found." + exit 1 + fi + TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXX)" trap cleanup_tmp_dir EXIT INT From 0340316050044e0995b98fea87ed41ea77abb28b Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Wed, 18 Aug 2010 15:23:16 -0700 Subject: [PATCH 012/225] Collect and send kernel crash diagnostics BUG=1512,1914 Review URL: http://codereview.chromium.org/3179006 --- crash_reporter/Makefile | 15 +- crash_reporter/crash_collector.cc | 150 ++++++++++++++ crash_reporter/crash_collector.h | 70 +++++++ crash_reporter/crash_collector_test.cc | 105 ++++++++++ crash_reporter/crash_reporter.cc | 164 ++++++++------- crash_reporter/crash_sender | 76 ++++--- crash_reporter/kernel_collector.cc | 118 +++++++++++ crash_reporter/kernel_collector.h | 54 +++++ crash_reporter/kernel_collector_test.cc | 186 ++++++++++++++++++ crash_reporter/unclean_shutdown_collector.cc | 57 ++++++ crash_reporter/unclean_shutdown_collector.h | 39 ++++ .../unclean_shutdown_collector_test.cc | 103 ++++++++++ crash_reporter/user_collector.cc | 147 ++------------ crash_reporter/user_collector.h | 25 +-- crash_reporter/user_collector_test.cc | 77 +------- 15 files changed, 1063 insertions(+), 323 deletions(-) create mode 100644 crash_reporter/crash_collector.cc create mode 100644 crash_reporter/crash_collector.h create mode 100644 crash_reporter/crash_collector_test.cc create mode 100644 crash_reporter/kernel_collector.cc create mode 100644 crash_reporter/kernel_collector.h create mode 100644 crash_reporter/kernel_collector_test.cc create mode 100644 crash_reporter/unclean_shutdown_collector.cc create mode 100644 crash_reporter/unclean_shutdown_collector.h create mode 100644 crash_reporter/unclean_shutdown_collector_test.cc diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index b51743d35..fc2d68013 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -4,9 +4,18 @@ CRASH_REPORTER = crash_reporter REPORTER_BINS = $(CRASH_REPORTER) -CRASH_OBJS = system_logging.o user_collector.o +CRASH_OBJS = \ + crash_collector.o \ + kernel_collector.o \ + system_logging.o \ + unclean_shutdown_collector.o \ + user_collector.o TEST_OBJS = $(CRASH_OBJS) system_logging_mock.o -TEST_BINS = user_collector_test +TEST_BINS = \ + crash_collector_test \ + kernel_collector_test \ + unclean_shutdown_collector_test \ + user_collector_test # -lglib-2.0 is needed by libbase.a now. COMMON_LIBS = -lbase -lpthread -lglib-2.0 -lgflags -lrt @@ -24,7 +33,7 @@ $(CRASH_REPORTER): crash_reporter.o $(CRASH_OBJS) tests: $(TEST_BINS) -user_collector_test: user_collector_test.o $(TEST_OBJS) +%_test: %_test.o $(TEST_OBJS) $(CXX) $(CXXFLAGS) $(LIB_DIRS) $^ $(TEST_LIBS) -o $@ .cc.o: diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc new file mode 100644 index 000000000..492f2e8bc --- /dev/null +++ b/crash_reporter/crash_collector.cc @@ -0,0 +1,150 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crash-reporter/crash_collector.h" + +#include // For struct passwd. +#include // for mode_t. + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "crash-reporter/system_logging.h" + +static const char kDefaultUserName[] = "chronos"; +static const char kSystemCrashPath[] = "/var/spool/crash"; +static const char kUserCrashPath[] = "/home/chronos/user/crash"; + +// Directory mode of the user crash spool directory. +static const mode_t kUserCrashPathMode = 0755; + +// Directory mode of the system crash spool directory. +static const mode_t kSystemCrashPathMode = 01755; + +static const uid_t kRootOwner = 0; +static const uid_t kRootGroup = 0; + +CrashCollector::CrashCollector() : forced_crash_directory_(NULL) { +} + +CrashCollector::~CrashCollector() { +} + +void CrashCollector::Initialize( + CrashCollector::CountCrashFunction count_crash_function, + CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function, + SystemLogging *logger) { + CHECK(count_crash_function != NULL); + CHECK(is_feedback_allowed_function != NULL); + CHECK(logger != NULL); + + count_crash_function_ = count_crash_function; + is_feedback_allowed_function_ = is_feedback_allowed_function; + logger_ = logger; +} + +std::string CrashCollector::FormatDumpBasename(const std::string &exec_name, + time_t timestamp, + pid_t pid) { + struct tm tm; + localtime_r(×tamp, &tm); + return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d", + exec_name.c_str(), + tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + pid); +} + +FilePath CrashCollector::GetCrashDirectoryInfo( + uid_t process_euid, + uid_t default_user_id, + gid_t default_user_group, + mode_t *mode, + uid_t *directory_owner, + gid_t *directory_group) { + if (process_euid == default_user_id) { + *mode = kUserCrashPathMode; + *directory_owner = default_user_id; + *directory_group = default_user_group; + return FilePath(kUserCrashPath); + } else { + *mode = kSystemCrashPathMode; + *directory_owner = kRootOwner; + *directory_group = kRootGroup; + return FilePath(kSystemCrashPath); + } +} + +bool CrashCollector::GetUserInfoFromName(const std::string &name, + uid_t *uid, + gid_t *gid) { + char storage[256]; + struct passwd passwd_storage; + struct passwd *passwd_result = NULL; + + if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage), + &passwd_result) != 0 || passwd_result == NULL) { + logger_->LogError("Cannot find user named %s", name.c_str()); + return false; + } + + *uid = passwd_result->pw_uid; + *gid = passwd_result->pw_gid; + return true; +} + +bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid, + FilePath *crash_directory) { + uid_t default_user_id; + gid_t default_user_group; + + // For testing. + if (forced_crash_directory_ != NULL) { + *crash_directory = FilePath(forced_crash_directory_); + return true; + } + + if (!GetUserInfoFromName(kDefaultUserName, + &default_user_id, + &default_user_group)) { + logger_->LogError("Could not find default user info"); + return false; + } + mode_t directory_mode; + uid_t directory_owner; + gid_t directory_group; + *crash_directory = + GetCrashDirectoryInfo(euid, + default_user_id, + default_user_group, + &directory_mode, + &directory_owner, + &directory_group); + + if (!file_util::PathExists(*crash_directory)) { + // Create the spool directory with the appropriate mode (regardless of + // umask) and ownership. + mode_t old_mask = umask(0); + if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 || + chown(crash_directory->value().c_str(), + directory_owner, + directory_group) < 0) { + logger_->LogError("Unable to create appropriate crash directory"); + return false; + } + umask(old_mask); + } + + if (!file_util::PathExists(*crash_directory)) { + logger_->LogError("Unable to create crash directory %s", + crash_directory->value().c_str()); + return false; + } + + return true; +} diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h new file mode 100644 index 000000000..ef0d2a7b1 --- /dev/null +++ b/crash_reporter/crash_collector.h @@ -0,0 +1,70 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CRASH_REPORTER_CRASH_COLLECTOR_H_ +#define _CRASH_REPORTER_CRASH_COLLECTOR_H_ + +#include +#include + +#include "gtest/gtest_prod.h" // for FRIEND_TEST + +class FilePath; +class SystemLogging; + +// User crash collector. +class CrashCollector { + public: + typedef void (*CountCrashFunction)(); + typedef bool (*IsFeedbackAllowedFunction)(); + + CrashCollector(); + + virtual ~CrashCollector(); + + // Initialize the crash collector for detection of crashes, given a + // crash counting function, metrics collection enabled oracle, and + // system logger facility. + void Initialize(CountCrashFunction count_crash, + IsFeedbackAllowedFunction is_metrics_allowed, + SystemLogging *logger); + + protected: + friend class CrashCollectorTest; + FRIEND_TEST(CrashCollectorTest, GetCrashDirectoryInfo); + FRIEND_TEST(CrashCollectorTest, FormatDumpBasename); + FRIEND_TEST(CrashCollectorTest, Initialize); + + // For testing, set the directory always returned by + // GetCreatedCrashDirectoryByEuid. + void ForceCrashDirectory(const char *forced_directory) { + forced_crash_directory_ = forced_directory; + } + + FilePath GetCrashDirectoryInfo(uid_t process_euid, + uid_t default_user_id, + gid_t default_user_group, + mode_t *mode, + uid_t *directory_owner, + gid_t *directory_group); + bool GetUserInfoFromName(const std::string &name, + uid_t *uid, + gid_t *gid); + // Determines the crash directory for given eud, and creates the + // directory if necessary with appropriate permissions. Returns + // true whether or not directory needed to be created, false on any + // failure. + bool GetCreatedCrashDirectoryByEuid(uid_t euid, + FilePath *crash_file_path); + std::string FormatDumpBasename(const std::string &exec_name, + time_t timestamp, + pid_t pid); + + CountCrashFunction count_crash_function_; + IsFeedbackAllowedFunction is_feedback_allowed_function_; + SystemLogging *logger_; + const char *forced_crash_directory_; +}; + +#endif // _CRASH_REPORTER_CRASH_COLLECTOR_H_ diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc new file mode 100644 index 000000000..48b7fa03c --- /dev/null +++ b/crash_reporter/crash_collector_test.cc @@ -0,0 +1,105 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/file_util.h" +#include "crash-reporter/crash_collector.h" +#include "crash-reporter/system_logging_mock.h" +#include "gflags/gflags.h" +#include "gtest/gtest.h" + +void CountCrash() { + ADD_FAILURE(); +} + +bool IsMetrics() { + ADD_FAILURE(); + return false; +} + +class CrashCollectorTest : public ::testing::Test { + void SetUp() { + collector_.Initialize(CountCrash, + IsMetrics, + &logging_); + } + protected: + SystemLoggingMock logging_; + CrashCollector collector_; + pid_t pid_; +}; + +TEST_F(CrashCollectorTest, Initialize) { + ASSERT_TRUE(CountCrash == collector_.count_crash_function_); + ASSERT_TRUE(IsMetrics == collector_.is_feedback_allowed_function_); + ASSERT_TRUE(&logging_ == collector_.logger_); +} + +TEST_F(CrashCollectorTest, GetCrashDirectoryInfo) { + FilePath path; + const int kRootUid = 0; + const int kRootGid = 0; + const int kNtpUid = 5; + const int kChronosUid = 1000; + const int kChronosGid = 1001; + const mode_t kExpectedSystemMode = 01755; + const mode_t kExpectedUserMode = 0755; + + mode_t directory_mode; + uid_t directory_owner; + gid_t directory_group; + + path = collector_.GetCrashDirectoryInfo(kRootUid, + kChronosUid, + kChronosGid, + &directory_mode, + &directory_owner, + &directory_group); + EXPECT_EQ("/var/spool/crash", path.value()); + EXPECT_EQ(kExpectedSystemMode, directory_mode); + EXPECT_EQ(kRootUid, directory_owner); + EXPECT_EQ(kRootGid, directory_group); + + path = collector_.GetCrashDirectoryInfo(kNtpUid, + kChronosUid, + kChronosGid, + &directory_mode, + &directory_owner, + &directory_group); + EXPECT_EQ("/var/spool/crash", path.value()); + EXPECT_EQ(kExpectedSystemMode, directory_mode); + EXPECT_EQ(kRootUid, directory_owner); + EXPECT_EQ(kRootGid, directory_group); + + path = collector_.GetCrashDirectoryInfo(kChronosUid, + kChronosUid, + kChronosGid, + &directory_mode, + &directory_owner, + &directory_group); + EXPECT_EQ("/home/chronos/user/crash", path.value()); + EXPECT_EQ(kExpectedUserMode, directory_mode); + EXPECT_EQ(kChronosUid, directory_owner); + EXPECT_EQ(kChronosGid, directory_group); +} + +TEST_F(CrashCollectorTest, FormatDumpBasename) { + struct tm tm = {0}; + tm.tm_sec = 15; + tm.tm_min = 50; + tm.tm_hour = 13; + tm.tm_mday = 23; + tm.tm_mon = 4; + tm.tm_year = 110; + tm.tm_isdst = -1; + std::string basename = + collector_.FormatDumpBasename("foo", mktime(&tm), 100); + ASSERT_EQ("foo.20100523.135015.100", basename); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 0930cea44..e850e3070 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -7,7 +7,9 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" +#include "crash-reporter/kernel_collector.h" #include "crash-reporter/system_logging.h" +#include "crash-reporter/unclean_shutdown_collector.h" #include "crash-reporter/user_collector.h" #include "gflags/gflags.h" #include "metrics/metrics_library.h" @@ -22,69 +24,52 @@ DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); #pragma GCC diagnostic error "-Wstrict-aliasing" static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; -static const char kEmpty[] = ""; +static const char kUserCrashSignal[] = + "org.chromium.CrashReporter.UserCrash"; static const char kUncleanShutdownFile[] = "/var/lib/crash_reporter/pending_clean_shutdown"; // Enumeration of kinds of crashes to be used in the CrashCounter histogram. enum CrashKinds { - kCrashKindKernel = 1, - kCrashKindUser = 2, + kCrashKindUncleanShutdown = 1, + kCrashKindUser = 2, + kCrashKindKernel = 3, kCrashKindMax }; static MetricsLibrary s_metrics_lib; static SystemLoggingImpl s_system_log; -static bool IsMetricsCollectionAllowed() { - // TODO(kmixter): Eventually check system tainted state and - // move this down in metrics library where it would be explicitly - // checked when asked to send stats. +static bool IsFeedbackAllowed() { + // Once crosbug.com/5814 is fixed, call the is opted in function + // here. return true; } -static void CheckUncleanShutdown() { - FilePath unclean_file_path(kUncleanShutdownFile); - if (!file_util::PathExists(unclean_file_path)) { - return; - } - s_system_log.LogWarning("Last shutdown was not clean"); - if (IsMetricsCollectionAllowed()) { - s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), - kCrashKindKernel, - kCrashKindMax); - } - if (!file_util::Delete(unclean_file_path, false)) { - s_system_log.LogError("Failed to delete unclean shutdown file %s", - kUncleanShutdownFile); - } - - // Touch a file to notify the metrics daemon that a kernel crash has - // been detected so that it can log the time since the last kernel - // crash. - static const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected"; - FilePath crash_detected(kKernelCrashDetectedFile); - file_util::WriteFile(crash_detected, kEmpty, 0); +static bool TouchFile(const FilePath &file_path) { + return file_util::WriteFile(file_path, "", 0) == 0; } -static bool PrepareUncleanShutdownCheck() { - FilePath file_path(kUncleanShutdownFile); - file_util::CreateDirectory(file_path.DirName()); - return file_util::WriteFile(file_path, kEmpty, 0) == 0; +static void CountKernelCrash() { + s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), + kCrashKindKernel, + kCrashKindMax); } -static void SignalCleanShutdown() { - s_system_log.LogInfo("Clean shutdown signalled"); - file_util::Delete(FilePath(kUncleanShutdownFile), false); +static void CountUncleanShutdown() { + s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), + kCrashKindUncleanShutdown, + kCrashKindMax); } static void CountUserCrash() { - CHECK(IsMetricsCollectionAllowed()); s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), kCrashKindUser, kCrashKindMax); - + std::string command = StringPrintf( + "/usr/bin/dbus-send --type=signal --system / \"%s\"", + kUserCrashSignal); // Announce through D-Bus whenever a user crash happens. This is // used by the metrics daemon to log active use time between // crashes. @@ -93,42 +78,47 @@ static void CountUserCrash() { // using a dbus library directly. However, this should run // relatively rarely and longer term we may need to implement a // better way to do this that doesn't rely on D-Bus. - int status __attribute__((unused)) = - system("/usr/bin/dbus-send --type=signal --system / " - "org.chromium.CrashReporter.UserCrash"); + + int status __attribute__((unused)) = system(command.c_str()); } -int main(int argc, char *argv[]) { - google::ParseCommandLineFlags(&argc, &argv, true); - FilePath my_path(argv[0]); - file_util::AbsolutePath(&my_path); - s_metrics_lib.Init(); - s_system_log.Initialize(my_path.BaseName().value().c_str()); - UserCollector user_collector; - user_collector.Initialize(CountUserCrash, - my_path.value(), - IsMetricsCollectionAllowed, - &s_system_log, - true); // generate_diagnostics +static int Initialize(KernelCollector *kernel_collector, + UserCollector *user_collector, + UncleanShutdownCollector *unclean_shutdown_collector) { + CHECK(!FLAGS_clean_shutdown) << "Incompatible options"; - if (FLAGS_init) { - CHECK(!FLAGS_clean_shutdown) << "Incompatible options"; - user_collector.Enable(); - if (FLAGS_unclean_check) { - CheckUncleanShutdown(); - if (!PrepareUncleanShutdownCheck()) { - s_system_log.LogError("Unable to create shutdown check file"); - } + bool was_kernel_crash = false; + bool was_unclean_shutdown = false; + if (kernel_collector->IsEnabled()) { + was_kernel_crash = kernel_collector->Collect(); + } + + if (FLAGS_unclean_check) { + was_unclean_shutdown = unclean_shutdown_collector->Collect(); + } + + // Touch a file to notify the metrics daemon that a kernel + // crash has been detected so that it can log the time since + // the last kernel crash. + if (IsFeedbackAllowed()) { + if (was_kernel_crash) { + TouchFile(FilePath("/tmp/kernel-crash-detected")); + } else if (was_unclean_shutdown) { + // We only count an unclean shutdown if it did not come with + // an associated kernel crash. + TouchFile(FilePath("/tmp/unclean-shutdown-detected")); } - return 0; } - if (FLAGS_clean_shutdown) { - SignalCleanShutdown(); - user_collector.Disable(); - return 0; - } + // Must enable the unclean shutdown collector *after* collecting. + kernel_collector->Enable(); + unclean_shutdown_collector->Enable(); + user_collector->Enable(); + return 0; +} + +static int HandleUserCrash(UserCollector *user_collector) { // Handle a specific user space crash. CHECK(FLAGS_signal != -1) << "Signal must be set"; CHECK(FLAGS_pid != -1) << "PID must be set"; @@ -141,9 +131,45 @@ int main(int argc, char *argv[]) { } // Handle the crash, get the name of the process from procfs. - if (!user_collector.HandleCrash(FLAGS_signal, FLAGS_pid, NULL)) { + if (!user_collector->HandleCrash(FLAGS_signal, FLAGS_pid, NULL)) { return 1; } - return 0; } + + +int main(int argc, char *argv[]) { + google::ParseCommandLineFlags(&argc, &argv, true); + FilePath my_path(argv[0]); + file_util::AbsolutePath(&my_path); + s_metrics_lib.Init(); + s_system_log.Initialize(my_path.BaseName().value().c_str()); + KernelCollector kernel_collector; + kernel_collector.Initialize(CountKernelCrash, + IsFeedbackAllowed, + &s_system_log); + UserCollector user_collector; + user_collector.Initialize(CountUserCrash, + my_path.value(), + IsFeedbackAllowed, + &s_system_log, + true); // generate_diagnostics + UncleanShutdownCollector unclean_shutdown_collector; + unclean_shutdown_collector.Initialize(CountUncleanShutdown, + IsFeedbackAllowed, + &s_system_log); + + if (FLAGS_init) { + return Initialize(&kernel_collector, + &user_collector, + &unclean_shutdown_collector); + } + + if (FLAGS_clean_shutdown) { + unclean_shutdown_collector.Disable(); + user_collector.Disable(); + return 0; + } + + return HandleUserCrash(&user_collector); +} diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 99cc0def3..5ada4ef17 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -19,18 +19,19 @@ FIND="/usr/bin/find" # Send up to 8 crashes per day. MAX_CRASH_RATE=8 -# URL to send non-official build crashes to. -MINIDUMP_UPLOAD_STAGING_URL="http://clients2.google.com/cr/staging_report" - -# URL to send official build crashes to. -MINIDUMP_UPLOAD_PROD_URL="http://clients2.google.com/cr/report" - # File whose existence mocks crash sending. If empty we pretend the # crash sending was successful, otherwise unsuccessful. MOCK_CRASH_SENDING="/tmp/mock-crash-sending" # File whose existence causes crash sending to be delayed (for testing). -PAUSE_CRASH_SENDING="/tmp/pause-crash-sending" +# Must be stateful to enable testing kernel crashes. +PAUSE_CRASH_SENDING="/var/lib/crash_sender_paused" + +# URL to send non-official build crash reports to. +REPORT_UPLOAD_STAGING_URL="http://clients2.google.com/cr/staging_report" + +# URL to send official build crash reports to. +REPORT_UPLOAD_PROD_URL="http://clients2.google.com/cr/report" # File whose existence implies we're running and not to start again. RUN_FILE="/var/run/crash_sender.pid" @@ -118,31 +119,35 @@ check_rate() { } # Return if $1 is a .core file -is_core_file() { - local filename=$1 - [ "${filename##*.}" = "core" ] +get_kind() { + local kind="${1##*.}" + if [ "${kind}" = "dmp" ]; then + kind="minidump" + fi + echo ${kind} +} + +get_exec_name() { + local filename=$(basename "$1") + echo "${filename%%.*}" } send_crash() { + local report_path="$1" + local kind=$(get_kind "${report_path}") + local exec_name=$(get_exec_name "${report_path}") local sleep_time=$(generate_uniform_random $SECONDS_SEND_SPREAD) - local url="${MINIDUMP_UPLOAD_STAGING_URL}" + local url="${REPORT_UPLOAD_STAGING_URL}" if is_official; then - url="${MINIDUMP_UPLOAD_PROD_URL}" + url="${REPORT_UPLOAD_PROD_URL}" fi lecho "Sending crash:" lecho " Scheduled to send in ${sleep_time}s" - lecho " Minidump: ${minidump_path}" + lecho " Report: ${report_path} (${kind})" lecho " URL: ${url}" lecho " Product: ${CHROMEOS_PRODUCT}" lecho " Version: ${chromeos_version}" - if [ -s "${minidump_path}" ]; then - # We cannot tell much from the minidump without symbols, but we can tell - # at least what modules were loaded at the time of crash - local modules="$(/usr/bin/minidump_dump "${minidump_path}" 2>&- | \ - grep 'code_file' | sed -e 's/^.* = "//g;s/"//g' | \ - tr '\n' ' ')" - lecho " Mapped: ${modules}" - fi + lecho " Exec name: ${exec_name}" if [ -f "${MOCK_CRASH_SENDING}" ]; then local mock_in=$(cat "${MOCK_CRASH_SENDING}") if [ "${mock_in}" = "" ]; then @@ -166,9 +171,10 @@ send_crash() { curl "${url}" \ -F "prod=${CHROMEOS_PRODUCT}" \ -F "ver=${chromeos_version}" \ - -F "upload_file_minidump=@${minidump_path}" \ + -F "upload_file_${kind}=@${report_path}" \ + -F "exec_name=${exec_name}" \ -F "guid=<${CONSENT_ID}" -o "${report_id}" 2>"${curl_stderr}" - local curl_result=$? + curl_result=$? set -e if [ ${curl_result} -eq 0 ]; then @@ -194,28 +200,34 @@ send_crashes() { return fi for file in $(ls -1t "${dir}"); do - local minidump_path="${dir}/${file}" - lecho "Considering file ${minidump_path}" - if is_core_file "${minidump_path}"; then + local report_path="${dir}/${file}" + lecho "Considering file ${report_path}" + local kind=$(get_kind "${report_path}") + + if [ "${kind}" = "core" ]; then lecho "Ignoring core file." continue + elif [ "${kind}" != "minidump" ] && [ "${kind}" != "kcrash" ]; then + lecho "Unknown report kind: ${kind}. Removing report." + rm -f "${report_path}" + continue fi if ! check_rate; then - lecho "Sending ${minidump_path} would exceed rate. Leaving for later." + lecho "Sending ${report_path} would exceed rate. Leaving for later." return 0 fi local chromeos_version=$(get_version) if is_feedback_disabled; then lecho "Uploading is disabled. Removing crash." - rm "${minidump_path}" + rm "${report_path}" elif is_on_3g; then lecho "Not sending crash report while on 3G, saving for later." - elif send_crash ${minidump_path}; then + elif send_crash "${report_path}"; then # Send was successful, now remove - lecho "Successfully sent crash ${minidump_path} and removing" - rm "${minidump_path}" + lecho "Successfully sent crash ${report_path} and removing" + rm "${report_path}" else - lecho "Problem sending ${minidump_path}, not removing" + lecho "Problem sending ${report_path}, not removing" fi done } diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc new file mode 100644 index 000000000..1a79c9d86 --- /dev/null +++ b/crash_reporter/kernel_collector.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crash-reporter/kernel_collector.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "crash-reporter/system_logging.h" + +static const char kKernelExecName[] = "kernel"; +static const char kPreservedDumpPath[] = "/sys/kernel/debug/preserved/kcrash"; +const pid_t kKernelPid = 0; +const uid_t kRootUid = 0; + +const char KernelCollector::kClearingSequence[] = " "; + +KernelCollector::KernelCollector() + : is_enabled_(false), + preserved_dump_path_(kPreservedDumpPath) { +} + +KernelCollector::~KernelCollector() { +} + +void KernelCollector::OverridePreservedDumpPath(const FilePath &file_path) { + preserved_dump_path_ = file_path; +} + +bool KernelCollector::LoadPreservedDump(std::string *contents) { + // clear contents since ReadFileToString actually appends to the string. + contents->clear(); + if (!file_util::ReadFileToString(preserved_dump_path_, contents)) { + logger_->LogError("Unable to read %s", + preserved_dump_path_.value().c_str()); + return false; + } + return true; +} + +bool KernelCollector::Enable() { + if (!file_util::PathExists(preserved_dump_path_)) { + logger_->LogWarning("Kernel does not support crash dumping"); + return false; + } + + // To enable crashes, we will eventually need to set + // the chnv bit in BIOS, but it does not yet work. + logger_->LogInfo("Enabling kernel crash handling"); + is_enabled_ = true; + return true; +} + +bool KernelCollector::ClearPreservedDump() { + // It is necessary to write at least one byte to the kcrash file for + // the log to actually be cleared. + if (file_util::WriteFile( + preserved_dump_path_, + kClearingSequence, + strlen(kClearingSequence)) != strlen(kClearingSequence)) { + logger_->LogError("Failed to clear kernel crash dump"); + return false; + } + logger_->LogInfo("Cleared kernel crash diagnostics"); + return true; +} + +FilePath KernelCollector::GetKernelCrashPath( + const FilePath &root_crash_path, + time_t timestamp) { + std::string dump_basename = + FormatDumpBasename(kKernelExecName, + timestamp, + kKernelPid); + return root_crash_path.Append( + StringPrintf("%s.kcrash", dump_basename.c_str())); +} + +bool KernelCollector::Collect() { + std::string kernel_dump; + FilePath root_crash_directory; + if (!LoadPreservedDump(&kernel_dump)) { + return false; + } + if (kernel_dump.empty()) { + return false; + } + if (is_feedback_allowed_function_()) { + count_crash_function_(); + + if (!GetCreatedCrashDirectoryByEuid(kRootUid, + &root_crash_directory)) { + return true; + } + + FilePath kernel_crash_path = GetKernelCrashPath(root_crash_directory, + time(NULL)); + if (file_util::WriteFile(kernel_crash_path, + kernel_dump.data(), + kernel_dump.length()) != + static_cast(kernel_dump.length())) { + logger_->LogInfo("Failed to write kernel dump to %s", + kernel_crash_path.value().c_str()); + return true; + } + + logger_->LogInfo("Collected kernel crash diagnostics into %s", + kernel_crash_path.value().c_str()); + } else { + logger_->LogInfo("Crash not saved since metrics disabled"); + } + if (!ClearPreservedDump()) { + return false; + } + + return true; +} diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h new file mode 100644 index 000000000..3e2944245 --- /dev/null +++ b/crash_reporter/kernel_collector.h @@ -0,0 +1,54 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CRASH_REPORTER_KERNEL_COLLECTOR_H_ +#define _CRASH_REPORTER_KERNEL_COLLECTOR_H_ + +#include + +#include "base/file_path.h" +#include "crash-reporter/crash_collector.h" +#include "gtest/gtest_prod.h" // for FRIEND_TEST + +class FilePath; + +// Kernel crash collector. +class KernelCollector : public CrashCollector { + public: + KernelCollector(); + + virtual ~KernelCollector(); + + void OverridePreservedDumpPath(const FilePath &file_path); + + // Enable collection. + bool Enable(); + + // Returns true if the kernel collection currently enabled. + bool IsEnabled() { + return is_enabled_; + } + + // Collect any preserved kernel crash dump. Returns true if there was + // a dump (even if there were problems storing the dump), false otherwise. + bool Collect(); + + private: + friend class KernelCollectorTest; + FRIEND_TEST(KernelCollectorTest, ClearPreservedDump); + FRIEND_TEST(KernelCollectorTest, GetKernelCrashPath); + FRIEND_TEST(KernelCollectorTest, LoadPreservedDump); + FRIEND_TEST(KernelCollectorTest, CollectOK); + + bool LoadPreservedDump(std::string *contents); + bool ClearPreservedDump(); + FilePath GetKernelCrashPath(const FilePath &root_crash_path, + time_t timestamp); + + bool is_enabled_; + FilePath preserved_dump_path_; + static const char kClearingSequence[]; +}; + +#endif // _CRASH_REPORTER_KERNEL_COLLECTOR_H_ diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc new file mode 100644 index 000000000..7188e2f15 --- /dev/null +++ b/crash_reporter/kernel_collector_test.cc @@ -0,0 +1,186 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/file_util.h" +#include "base/string_util.h" +#include "crash-reporter/kernel_collector.h" +#include "crash-reporter/system_logging_mock.h" +#include "gflags/gflags.h" +#include "gtest/gtest.h" + +static int s_crashes = 0; +static bool s_metrics = false; + +static const char kTestKCrash[] = "test/kcrash"; +static const char kTestCrashDirectory[] = "test/crash_directory"; + +void CountCrash() { + ++s_crashes; +} + +bool IsMetrics() { + return s_metrics; +} + +class KernelCollectorTest : public ::testing::Test { + void SetUp() { + s_crashes = 0; + s_metrics = true; + collector_.Initialize(CountCrash, + IsMetrics, + &logging_); + mkdir("test", 0777); + test_kcrash_ = FilePath(kTestKCrash); + collector_.OverridePreservedDumpPath(test_kcrash_); + unlink(kTestKCrash); + mkdir(kTestCrashDirectory, 0777); + } + protected: + void WriteStringToFile(const FilePath &file_path, + const char *data) { + ASSERT_EQ(strlen(data), + file_util::WriteFile(file_path, data, strlen(data))); + } + + void SetUpSuccessfulCollect(); + void CheckPreservedDumpClear(); + + SystemLoggingMock logging_; + KernelCollector collector_; + FilePath test_kcrash_; +}; + +TEST_F(KernelCollectorTest, LoadPreservedDump) { + ASSERT_FALSE(file_util::PathExists(test_kcrash_)); + std::string dump; + ASSERT_FALSE(collector_.LoadPreservedDump(&dump)); + WriteStringToFile(test_kcrash_, ""); + ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); + ASSERT_EQ("", dump); + WriteStringToFile(test_kcrash_, "something"); + ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); + ASSERT_EQ("something", dump); +} + +TEST_F(KernelCollectorTest, EnableMissingKernel) { + ASSERT_FALSE(collector_.Enable()); + ASSERT_FALSE(collector_.IsEnabled()); + ASSERT_EQ(std::string::npos, + logging_.log().find("Enabling kernel crash handling")); + ASSERT_NE(std::string::npos, + logging_.log().find("Kernel does not support crash dumping")); + ASSERT_EQ(s_crashes, 0); +} + +TEST_F(KernelCollectorTest, EnableOK) { + WriteStringToFile(test_kcrash_, ""); + ASSERT_TRUE(collector_.Enable()); + ASSERT_TRUE(collector_.IsEnabled()); + ASSERT_NE(std::string::npos, + logging_.log().find("Enabling kernel crash handling")); + ASSERT_EQ(s_crashes, 0); +} + +TEST_F(KernelCollectorTest, ClearPreservedDump) { + std::string dump; + ASSERT_FALSE(file_util::PathExists(test_kcrash_)); + WriteStringToFile(test_kcrash_, "something"); + ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); + ASSERT_EQ("something", dump); + ASSERT_TRUE(collector_.ClearPreservedDump()); + ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); + ASSERT_EQ(KernelCollector::kClearingSequence, dump); +} + +TEST_F(KernelCollectorTest, GetKernelCrashPath) { + FilePath root("/var/spool/crash"); + struct tm tm = {0}; + tm.tm_sec = 15; + tm.tm_min = 50; + tm.tm_hour = 13; + tm.tm_mday = 23; + tm.tm_mon = 4; + tm.tm_year = 110; + tm.tm_isdst = -1; + FilePath path = collector_.GetKernelCrashPath(root, mktime(&tm)); + ASSERT_EQ("/var/spool/crash/kernel.20100523.135015.0.kcrash", + path.value()); +} + +TEST_F(KernelCollectorTest, CollectPreservedFileMissing) { + ASSERT_FALSE(collector_.Collect()); + ASSERT_NE(logging_.log().find("Unable to read test/kcrash"), + std::string::npos); + ASSERT_EQ(0, s_crashes); +} + +TEST_F(KernelCollectorTest, CollectNoCrash) { + WriteStringToFile(test_kcrash_, ""); + ASSERT_FALSE(collector_.Collect()); + ASSERT_EQ(logging_.log().find("Collected kernel crash"), + std::string::npos); + ASSERT_EQ(0, s_crashes); +} + +TEST_F(KernelCollectorTest, CollectBadDirectory) { + WriteStringToFile(test_kcrash_, "something"); + ASSERT_TRUE(collector_.Collect()); + ASSERT_NE(logging_.log().find( + "Unable to create appropriate crash directory"), std::string::npos); + ASSERT_EQ(1, s_crashes); +} + +void KernelCollectorTest::SetUpSuccessfulCollect() { + collector_.ForceCrashDirectory(kTestCrashDirectory); + WriteStringToFile(test_kcrash_, "something"); + ASSERT_EQ(0, s_crashes); +} + +void KernelCollectorTest::CheckPreservedDumpClear() { + // Make sure the preserved dump is now clear. + std::string dump; + ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); + ASSERT_EQ(KernelCollector::kClearingSequence, dump); +} + +TEST_F(KernelCollectorTest, CollectOptedOut) { + SetUpSuccessfulCollect(); + s_metrics = false; + ASSERT_TRUE(collector_.Collect()); + ASSERT_NE(std::string::npos, + logging_.log().find("Crash not saved since metrics disabled")); + ASSERT_EQ(0, s_crashes); + + CheckPreservedDumpClear(); +} + + +TEST_F(KernelCollectorTest, CollectOK) { + SetUpSuccessfulCollect(); + ASSERT_TRUE(collector_.Collect()); + ASSERT_EQ(1, s_crashes); + static const char kNamePrefix[] = "Collected kernel crash diagnostics into "; + size_t pos = logging_.log().find(kNamePrefix); + ASSERT_NE(std::string::npos, pos); + pos += strlen(kNamePrefix); + std::string filename = logging_.log().substr(pos, std::string::npos); + // Take the name up until \n + size_t end_pos = filename.find_first_of("\n"); + ASSERT_NE(std::string::npos, end_pos); + filename = filename.substr(0, end_pos); + ASSERT_EQ(0, filename.find(kTestCrashDirectory)); + ASSERT_TRUE(file_util::PathExists(FilePath(filename))); + std::string contents; + ASSERT_TRUE(file_util::ReadFileToString(FilePath(filename), &contents)); + ASSERT_EQ("something", contents); + + CheckPreservedDumpClear(); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc new file mode 100644 index 000000000..bb2dd7a86 --- /dev/null +++ b/crash_reporter/unclean_shutdown_collector.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crash-reporter/unclean_shutdown_collector.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "crash-reporter/system_logging.h" + +static const char kUncleanShutdownFile[] = + "/var/lib/crash_reporter/pending_clean_shutdown"; + +UncleanShutdownCollector::UncleanShutdownCollector() + : unclean_shutdown_file_(kUncleanShutdownFile) { +} + +UncleanShutdownCollector::~UncleanShutdownCollector() { +} + +bool UncleanShutdownCollector::Enable() { + FilePath file_path(unclean_shutdown_file_); + file_util::CreateDirectory(file_path.DirName()); + if (file_util::WriteFile(file_path, "", 0) != 0) { + logger_->LogError("Unable to create shutdown check file"); + return false; + } + return true; +} + +bool UncleanShutdownCollector::DeleteUncleanShutdownFile() { + if (!file_util::Delete(FilePath(unclean_shutdown_file_), false)) { + logger_->LogError("Failed to delete unclean shutdown file %s", + unclean_shutdown_file_); + return false; + } + return true; +} + +bool UncleanShutdownCollector::Collect() { + FilePath unclean_file_path(unclean_shutdown_file_); + if (!file_util::PathExists(unclean_file_path)) { + return false; + } + logger_->LogWarning("Last shutdown was not clean"); + DeleteUncleanShutdownFile(); + + if (is_feedback_allowed_function_()) { + count_crash_function_(); + } + return true; +} + +bool UncleanShutdownCollector::Disable() { + logger_->LogInfo("Clean shutdown signalled"); + return DeleteUncleanShutdownFile(); +} diff --git a/crash_reporter/unclean_shutdown_collector.h b/crash_reporter/unclean_shutdown_collector.h new file mode 100644 index 000000000..e7d8cef95 --- /dev/null +++ b/crash_reporter/unclean_shutdown_collector.h @@ -0,0 +1,39 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_ +#define _CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_ + +#include + +#include "base/file_path.h" +#include "crash-reporter/crash_collector.h" +#include "gtest/gtest_prod.h" // for FRIEND_TEST + +// Unclean shutdown collector. +class UncleanShutdownCollector : public CrashCollector { + public: + UncleanShutdownCollector(); + virtual ~UncleanShutdownCollector(); + + // Enable collection - signal that a boot has started. + bool Enable(); + + // Collect if there is was an unclean shutdown. Returns true if + // there was, false otherwise. + bool Collect(); + + // Disable collection - signal that the system has been shutdown cleanly. + bool Disable(); + + private: + friend class UncleanShutdownCollectorTest; + FRIEND_TEST(UncleanShutdownCollectorTest, EnableCannotWrite); + + bool DeleteUncleanShutdownFile(); + + const char *unclean_shutdown_file_; +}; + +#endif // _CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_ diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc new file mode 100644 index 000000000..7be52af14 --- /dev/null +++ b/crash_reporter/unclean_shutdown_collector_test.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/file_util.h" +#include "base/string_util.h" +#include "crash-reporter/unclean_shutdown_collector.h" +#include "crash-reporter/system_logging_mock.h" +#include "gflags/gflags.h" +#include "gtest/gtest.h" + +static int s_crashes = 0; +static bool s_metrics = false; + +static const char kTestUnclean[] = "test/unclean"; + +void CountCrash() { + ++s_crashes; +} + +bool IsMetrics() { + return s_metrics; +} + +class UncleanShutdownCollectorTest : public ::testing::Test { + void SetUp() { + s_crashes = 0; + collector_.Initialize(CountCrash, + IsMetrics, + &logging_); + rmdir("test"); + test_unclean_ = FilePath(kTestUnclean); + collector_.unclean_shutdown_file_ = kTestUnclean; + file_util::Delete(test_unclean_, true); + } + protected: + void WriteStringToFile(const FilePath &file_path, + const char *data) { + ASSERT_EQ(strlen(data), + file_util::WriteFile(file_path, data, strlen(data))); + } + + SystemLoggingMock logging_; + UncleanShutdownCollector collector_; + FilePath test_unclean_; +}; + +TEST_F(UncleanShutdownCollectorTest, EnableWithoutParent) { + ASSERT_TRUE(collector_.Enable()); + ASSERT_TRUE(file_util::PathExists(test_unclean_)); +} + +TEST_F(UncleanShutdownCollectorTest, EnableWithParent) { + mkdir("test", 0777); + ASSERT_TRUE(collector_.Enable()); + ASSERT_TRUE(file_util::PathExists(test_unclean_)); +} + +TEST_F(UncleanShutdownCollectorTest, EnableCannotWrite) { + collector_.unclean_shutdown_file_ = "/bad/path"; + ASSERT_FALSE(collector_.Enable()); + ASSERT_NE(std::string::npos, + logging_.log().find("Unable to create shutdown check file")); +} + +TEST_F(UncleanShutdownCollectorTest, CollectTrue) { + ASSERT_TRUE(collector_.Enable()); + ASSERT_TRUE(file_util::PathExists(test_unclean_)); + ASSERT_TRUE(collector_.Collect()); + ASSERT_FALSE(file_util::PathExists(test_unclean_)); + ASSERT_NE(std::string::npos, + logging_.log().find("Last shutdown was not clean")); +} + +TEST_F(UncleanShutdownCollectorTest, CollectFalse) { + ASSERT_FALSE(collector_.Collect()); +} + +TEST_F(UncleanShutdownCollectorTest, Disable) { + ASSERT_TRUE(collector_.Enable()); + ASSERT_TRUE(file_util::PathExists(test_unclean_)); + ASSERT_TRUE(collector_.Disable()); + ASSERT_FALSE(file_util::PathExists(test_unclean_)); + ASSERT_FALSE(collector_.Collect()); +} + +TEST_F(UncleanShutdownCollectorTest, DisableWhenNotEnabled) { + ASSERT_TRUE(collector_.Disable()); +} + +TEST_F(UncleanShutdownCollectorTest, CantDisable) { + mkdir(kTestUnclean, 0700); + file_util::WriteFile(test_unclean_.Append("foo"), "", 0); + ASSERT_FALSE(collector_.Disable()); + rmdir(kTestUnclean); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 18ff39a09..e051a4ac3 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "crash-reporter/user_collector.h" + #include // For struct group. #include // For struct passwd. #include // For getpwuid_r and getgrnam_r. @@ -11,27 +13,14 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" -#include "crash-reporter/user_collector.h" -#include "metrics/metrics_library.h" +#include "crash-reporter/system_logging.h" // This procfs file is used to cause kernel core file writing to // instead pipe the core file into a user space process. See // core(5) man page. static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern"; static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; -static const char kDefaultUserName[] = "chronos"; static const char kLeaveCoreFile[] = "/root/.leave_core"; -static const char kSystemCrashPath[] = "/var/spool/crash"; -static const char kUserCrashPath[] = "/home/chronos/user/crash"; - -// Directory mode of the user crash spool directory. -static const mode_t kUserCrashPathMode = 0755; - -// Directory mode of the system crash spool directory. -static const mode_t kSystemCrashPathMode = 01755; - -static const uid_t kRootOwner = 0; -static const uid_t kRootGroup = 0; const char *UserCollector::kUserId = "Uid:\t"; const char *UserCollector::kGroupId = "Gid:\t"; @@ -39,10 +28,7 @@ const char *UserCollector::kGroupId = "Gid:\t"; UserCollector::UserCollector() : generate_diagnostics_(false), core_pattern_file_(kCorePatternFile), - count_crash_function_(NULL), - initialized_(false), - is_feedback_allowed_function_(NULL), - logger_(NULL) { + initialized_(false) { } void UserCollector::Initialize( @@ -51,14 +37,10 @@ void UserCollector::Initialize( UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function, SystemLogging *logger, bool generate_diagnostics) { - CHECK(count_crash_function != NULL); - CHECK(is_feedback_allowed_function != NULL); - CHECK(logger != NULL); - - count_crash_function_ = count_crash_function; + CrashCollector::Initialize(count_crash_function, + is_feedback_allowed_function, + logger); our_path_ = our_path; - is_feedback_allowed_function_ = is_feedback_allowed_function; - logger_ = logger; initialized_ = true; generate_diagnostics_ = generate_diagnostics; } @@ -76,7 +58,8 @@ std::string UserCollector::GetPattern(bool enabled) const { bool UserCollector::SetUpInternal(bool enabled) { CHECK(initialized_); - logger_->LogInfo("%s crash handling", enabled ? "Enabling" : "Disabling"); + logger_->LogInfo("%s user crash handling", + enabled ? "Enabling" : "Disabling"); std::string pattern = GetPattern(enabled); if (file_util::WriteFile(FilePath(core_pattern_file_), pattern.c_str(), @@ -161,24 +144,6 @@ bool UserCollector::GetIdFromStatus(const char *prefix, return true; } -bool UserCollector::GetUserInfoFromName(const std::string &name, - uid_t *uid, - gid_t *gid) { - char storage[256]; - struct passwd passwd_storage; - struct passwd *passwd_result = NULL; - - if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage), - &passwd_result) != 0 || passwd_result == NULL) { - logger_->LogError("Cannot find user named %s", name.c_str()); - return false; - } - - *uid = passwd_result->pw_uid; - *gid = passwd_result->pw_gid; - return true; -} - bool UserCollector::CopyOffProcFiles(pid_t pid, const FilePath &container_dir) { if (!file_util::CreateDirectory(container_dir)) { @@ -208,26 +173,6 @@ bool UserCollector::CopyOffProcFiles(pid_t pid, return true; } -FilePath UserCollector::GetCrashDirectoryInfo( - uid_t process_euid, - uid_t default_user_id, - gid_t default_user_group, - mode_t *mode, - uid_t *directory_owner, - gid_t *directory_group) { - if (process_euid == default_user_id) { - *mode = kUserCrashPathMode; - *directory_owner = default_user_id; - *directory_group = default_user_group; - return FilePath(kUserCrashPath); - } else { - *mode = kSystemCrashPathMode; - *directory_owner = kRootOwner; - *directory_group = kRootGroup; - return FilePath(kSystemCrashPath); - } -} - bool UserCollector::GetCreatedCrashDirectory(pid_t pid, FilePath *crash_file_path) { FilePath process_path = GetProcessPath(pid); @@ -242,64 +187,8 @@ bool UserCollector::GetCreatedCrashDirectory(pid_t pid, logger_->LogError("Could not find euid in status file"); return false; } - uid_t default_user_id; - gid_t default_user_group; - if (!GetUserInfoFromName(kDefaultUserName, - &default_user_id, - &default_user_group)) { - logger_->LogError("Could not find default user info"); - return false; - } - mode_t directory_mode; - uid_t directory_owner; - gid_t directory_group; - *crash_file_path = - GetCrashDirectoryInfo(process_euid, - default_user_id, - default_user_group, - &directory_mode, - &directory_owner, - &directory_group); - - - if (!file_util::PathExists(*crash_file_path)) { - // Create the spool directory with the appropriate mode (regardless of - // umask) and ownership. - mode_t old_mask = umask(0); - if (mkdir(crash_file_path->value().c_str(), directory_mode) < 0 || - chown(crash_file_path->value().c_str(), - directory_owner, - directory_group) < 0) { - logger_->LogError("Unable to create appropriate crash directory"); - return false; - } - umask(old_mask); - } - - if (!file_util::PathExists(*crash_file_path)) { - logger_->LogError("Unable to create crash directory %s", - crash_file_path->value().c_str()); - return false; - } - - - return true; -} - -std::string UserCollector::FormatDumpBasename(const std::string &exec_name, - time_t timestamp, - pid_t pid) { - struct tm tm; - localtime_r(×tamp, &tm); - return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d", - exec_name.c_str(), - tm.tm_year + 1900, - tm.tm_mon + 1, - tm.tm_mday, - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - pid); + return GetCreatedCrashDirectoryByEuid(process_euid, + crash_file_path); } bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { @@ -360,13 +249,13 @@ bool UserCollector::GenerateDiagnostics(pid_t pid, return false; } - FilePath spool_path; - if (!GetCreatedCrashDirectory(pid, &spool_path)) { + FilePath crash_path; + if (!GetCreatedCrashDirectory(pid, &crash_path)) { file_util::Delete(container_dir, true); return false; } std::string dump_basename = FormatDumpBasename(exec_name, time(NULL), pid); - FilePath core_path = spool_path.Append( + FilePath core_path = crash_path.Append( StringPrintf("%s.core", dump_basename.c_str())); if (!CopyStdinToCoreFile(core_path)) { @@ -374,7 +263,7 @@ bool UserCollector::GenerateDiagnostics(pid_t pid, return false; } - FilePath minidump_path = spool_path.Append( + FilePath minidump_path = crash_path.Append( StringPrintf("%s.dmp", dump_basename.c_str())); bool conversion_result = true; @@ -414,10 +303,10 @@ bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { if (is_feedback_allowed_function_()) { count_crash_function_(); - } - if (generate_diagnostics_) { - return GenerateDiagnostics(pid, exec); + if (generate_diagnostics_) { + return GenerateDiagnostics(pid, exec); + } } return true; } diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index dc633bba4..484445fcb 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -7,17 +7,15 @@ #include -#include "crash-reporter/system_logging.h" +#include "crash-reporter/crash_collector.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST class FilePath; +class SystemLogging; // User crash collector. -class UserCollector { +class UserCollector : public CrashCollector { public: - typedef void (*CountCrashFunction)(); - typedef bool (*IsFeedbackAllowedFunction)(); - UserCollector(); // Initialize the user crash collector for detection of crashes, @@ -53,8 +51,6 @@ class UserCollector { FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPath); FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPid); FRIEND_TEST(UserCollectorTest, CopyOffProcFilesOK); - FRIEND_TEST(UserCollectorTest, FormatDumpBasename); - FRIEND_TEST(UserCollectorTest, GetCrashDirectoryInfo); FRIEND_TEST(UserCollectorTest, GetIdFromStatus); FRIEND_TEST(UserCollectorTest, GetProcessPath); FRIEND_TEST(UserCollectorTest, GetSymlinkTarget); @@ -82,25 +78,13 @@ class UserCollector { IdKind kind, const std::string &status_contents, int *id); - bool GetUserInfoFromName(const std::string &name, - uid_t *uid, - gid_t *gid); bool CopyOffProcFiles(pid_t pid, const FilePath &process_map); - FilePath GetCrashDirectoryInfo(uid_t process_euid, - uid_t default_user_id, - gid_t default_user_group, - mode_t *mode, - uid_t *directory_owner, - gid_t *directory_group); // Determines the crash directory for given pid based on pid's owner, // and creates the directory if necessary with appropriate permissions. // Returns true whether or not directory needed to be created, false on // any failure. bool GetCreatedCrashDirectory(pid_t pid, FilePath *crash_file_path); - std::string FormatDumpBasename(const std::string &exec_name, - time_t timestamp, - pid_t pid); bool CopyStdinToCoreFile(const FilePath &core_path); bool ConvertCoreToMinidump(const FilePath &core_path, const FilePath &procfs_directory, @@ -110,11 +94,8 @@ class UserCollector { bool generate_diagnostics_; std::string core_pattern_file_; - CountCrashFunction count_crash_function_; std::string our_path_; bool initialized_; - IsFeedbackAllowedFunction is_feedback_allowed_function_; - SystemLogging *logger_; static const char *kUserId; static const char *kGroupId; diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 335821c74..3390caab9 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -10,8 +10,8 @@ #include "gflags/gflags.h" #include "gtest/gtest.h" -int s_crashes = 0; -bool s_metrics = false; +static int s_crashes = 0; +static bool s_metrics = false; static const char kFilePath[] = "/my/path"; @@ -50,14 +50,16 @@ TEST_F(UserCollectorTest, EnableOK) { &contents)); ASSERT_EQ("|/my/path --signal=%s --pid=%p", contents); ASSERT_EQ(s_crashes, 0); - ASSERT_NE(logging_.log().find("Enabling crash handling"), std::string::npos); + ASSERT_NE(logging_.log().find("Enabling user crash handling"), + std::string::npos); } TEST_F(UserCollectorTest, EnableNoFileAccess) { collector_.set_core_pattern_file("/does_not_exist"); ASSERT_FALSE(collector_.Enable()); ASSERT_EQ(s_crashes, 0); - ASSERT_NE(logging_.log().find("Enabling crash handling"), std::string::npos); + ASSERT_NE(logging_.log().find("Enabling user crash handling"), + std::string::npos); ASSERT_NE(logging_.log().find("Unable to write /does_not_exist"), std::string::npos); } @@ -69,7 +71,7 @@ TEST_F(UserCollectorTest, DisableOK) { &contents)); ASSERT_EQ("core", contents); ASSERT_EQ(s_crashes, 0); - ASSERT_NE(logging_.log().find("Disabling crash handling"), + ASSERT_NE(logging_.log().find("Disabling user crash handling"), std::string::npos); } @@ -77,7 +79,8 @@ TEST_F(UserCollectorTest, DisableNoFileAccess) { collector_.set_core_pattern_file("/does_not_exist"); ASSERT_FALSE(collector_.Disable()); ASSERT_EQ(s_crashes, 0); - ASSERT_NE(logging_.log().find("Disabling crash handling"), std::string::npos); + ASSERT_NE(logging_.log().find("Disabling user crash handling"), + std::string::npos); ASSERT_NE(logging_.log().find("Unable to write /does_not_exist"), std::string::npos); } @@ -206,54 +209,6 @@ TEST_F(UserCollectorTest, GetUserInfoFromName) { EXPECT_EQ(0, gid); } -TEST_F(UserCollectorTest, GetCrashDirectoryInfo) { - FilePath path; - const int kRootUid = 0; - const int kRootGid = 0; - const int kNtpUid = 5; - const int kChronosUid = 1000; - const int kChronosGid = 1001; - const mode_t kExpectedSystemMode = 01755; - const mode_t kExpectedUserMode = 0755; - - mode_t directory_mode; - uid_t directory_owner; - gid_t directory_group; - - path = collector_.GetCrashDirectoryInfo(kRootUid, - kChronosUid, - kChronosGid, - &directory_mode, - &directory_owner, - &directory_group); - EXPECT_EQ("/var/spool/crash", path.value()); - EXPECT_EQ(kExpectedSystemMode, directory_mode); - EXPECT_EQ(kRootUid, directory_owner); - EXPECT_EQ(kRootGid, directory_group); - - path = collector_.GetCrashDirectoryInfo(kNtpUid, - kChronosUid, - kChronosGid, - &directory_mode, - &directory_owner, - &directory_group); - EXPECT_EQ("/var/spool/crash", path.value()); - EXPECT_EQ(kExpectedSystemMode, directory_mode); - EXPECT_EQ(kRootUid, directory_owner); - EXPECT_EQ(kRootGid, directory_group); - - path = collector_.GetCrashDirectoryInfo(kChronosUid, - kChronosUid, - kChronosGid, - &directory_mode, - &directory_owner, - &directory_group); - EXPECT_EQ("/home/chronos/user/crash", path.value()); - EXPECT_EQ(kExpectedUserMode, directory_mode); - EXPECT_EQ(kChronosUid, directory_owner); - EXPECT_EQ(kChronosGid, directory_group); -} - TEST_F(UserCollectorTest, CopyOffProcFilesBadPath) { // Try a path that is not writable. ASSERT_FALSE(collector_.CopyOffProcFiles(pid_, FilePath("/bad/path"))); @@ -295,20 +250,6 @@ TEST_F(UserCollectorTest, CopyOffProcFilesOK) { } } -TEST_F(UserCollectorTest, FormatDumpBasename) { - struct tm tm = {0}; - tm.tm_sec = 15; - tm.tm_min = 50; - tm.tm_hour = 13; - tm.tm_mday = 23; - tm.tm_mon = 4; - tm.tm_year = 110; - tm.tm_isdst = -1; - std::string basename = - collector_.FormatDumpBasename("foo", mktime(&tm), 100); - ASSERT_EQ("foo.20100523.135015.100", basename); -} - int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From 04ec10fc93b08057657503559ecf511661b55c9f Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Thu, 26 Aug 2010 16:02:02 -0700 Subject: [PATCH 013/225] Limit the number of crash reports to enqueue at once BUG=5357 Change-Id: Ib21086cdd34c938def885d625a165ba2fa0879a4 Review URL: http://codereview.chromium.org/3209003 --- crash_reporter/crash_collector.cc | 45 +++++++++++++++++++++ crash_reporter/crash_collector.h | 13 +++++- crash_reporter/crash_collector_test.cc | 56 +++++++++++++++++++++++++- 3 files changed, 112 insertions(+), 2 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 492f2e8bc..0194fc41c 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -4,6 +4,7 @@ #include "crash-reporter/crash_collector.h" +#include #include // For struct passwd. #include // for mode_t. @@ -25,6 +26,9 @@ static const mode_t kSystemCrashPathMode = 01755; static const uid_t kRootOwner = 0; static const uid_t kRootGroup = 0; +// Maximum of 8 crash reports per directory. +const int CrashCollector::kMaxCrashDirectorySize = 8; + CrashCollector::CrashCollector() : forced_crash_directory_(NULL) { } @@ -146,5 +150,46 @@ bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid, return false; } + if (!CheckHasCapacity(*crash_directory)) { + return false; + } + return true; } + +// Return true if the given crash directory has not already reached +// maximum capacity. +bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) { + DIR* dir = opendir(crash_directory.value().c_str()); + if (!dir) { + return false; + } + struct dirent ent_buf; + struct dirent* ent; + int count_non_core = 0; + int count_core = 0; + bool full = false; + while (readdir_r(dir, &ent_buf, &ent) == 0 && ent != NULL) { + if ((strcmp(ent->d_name, ".") == 0) || + (strcmp(ent->d_name, "..") == 0)) + continue; + + if (strcmp(ent->d_name + strlen(ent->d_name) - 5, ".core") == 0) { + ++count_core; + } else { + ++count_non_core; + } + + if (count_core >= kMaxCrashDirectorySize || + count_non_core >= kMaxCrashDirectorySize) { + logger_->LogWarning( + "Crash directory %s already full with %d pending reports", + crash_directory.value().c_str(), + kMaxCrashDirectorySize); + full = true; + break; + } + } + closedir(dir); + return !full; +} diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index ef0d2a7b1..a75d79148 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -32,10 +32,15 @@ class CrashCollector { protected: friend class CrashCollectorTest; + FRIEND_TEST(CrashCollectorTest, CheckHasCapacityOverCore); + FRIEND_TEST(CrashCollectorTest, CheckHasCapacityOverNonCore); FRIEND_TEST(CrashCollectorTest, GetCrashDirectoryInfo); FRIEND_TEST(CrashCollectorTest, FormatDumpBasename); FRIEND_TEST(CrashCollectorTest, Initialize); + // Set maximum enqueued crashes in a crash directory. + static const int kMaxCrashDirectorySize; + // For testing, set the directory always returned by // GetCreatedCrashDirectoryByEuid. void ForceCrashDirectory(const char *forced_directory) { @@ -54,13 +59,19 @@ class CrashCollector { // Determines the crash directory for given eud, and creates the // directory if necessary with appropriate permissions. Returns // true whether or not directory needed to be created, false on any - // failure. + // failure. If the crash directory is already full, returns false. bool GetCreatedCrashDirectoryByEuid(uid_t euid, FilePath *crash_file_path); + + // Format crash name based on components. std::string FormatDumpBasename(const std::string &exec_name, time_t timestamp, pid_t pid); + // Check given crash directory still has remaining capacity for another + // crash. + bool CheckHasCapacity(const FilePath &crash_directory); + CountCrashFunction count_crash_function_; IsFeedbackAllowedFunction is_feedback_allowed_function_; SystemLogging *logger_; diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 48b7fa03c..ed8fff756 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -5,6 +5,7 @@ #include #include "base/file_util.h" +#include "base/string_util.h" #include "crash-reporter/crash_collector.h" #include "crash-reporter/system_logging_mock.h" #include "gflags/gflags.h" @@ -20,15 +21,25 @@ bool IsMetrics() { } class CrashCollectorTest : public ::testing::Test { + public: void SetUp() { collector_.Initialize(CountCrash, IsMetrics, &logging_); + test_dir_ = FilePath("test"); + file_util::CreateDirectory(test_dir_); } + + void TearDown() { + file_util::Delete(test_dir_, true); + } + + bool CheckHasCapacity(); + protected: SystemLoggingMock logging_; CrashCollector collector_; - pid_t pid_; + FilePath test_dir_; }; TEST_F(CrashCollectorTest, Initialize) { @@ -99,6 +110,49 @@ TEST_F(CrashCollectorTest, FormatDumpBasename) { ASSERT_EQ("foo.20100523.135015.100", basename); } +bool CrashCollectorTest::CheckHasCapacity() { + static const char kFullMessage[] = "Crash directory test already full"; + bool has_capacity = collector_.CheckHasCapacity(test_dir_); + bool has_message = (logging_.log().find(kFullMessage) != std::string::npos); + EXPECT_EQ(has_message, !has_capacity); + return has_capacity; +} + +TEST_F(CrashCollectorTest, CheckHasCapacityOverNonCore) { + // Test up to kMaxCrashDirectorySize-1 non-core files can be added. + for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { + EXPECT_TRUE(CheckHasCapacity()); + file_util::WriteFile(test_dir_.Append(StringPrintf("file%d", i)), "", 0); + } + + // Test an additional kMaxCrashDirectorySize - 1 core files fit. + for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { + EXPECT_TRUE(CheckHasCapacity()); + file_util::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)), + "", 0); + } + + // Test an additional kMaxCrashDirectorySize non-core files don't fit. + for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) { + file_util::WriteFile(test_dir_.Append(StringPrintf("overage%d", i)), "", 0); + EXPECT_FALSE(CheckHasCapacity()); + } +} + +TEST_F(CrashCollectorTest, CheckHasCapacityOverCore) { + // Set up kMaxCrashDirectorySize - 1 core files. + for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { + file_util::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)), + "", 0); + } + + EXPECT_TRUE(CheckHasCapacity()); + + // Test an additional core file does not fit. + file_util::WriteFile(test_dir_.Append("overage.core"), "", 0); + EXPECT_FALSE(CheckHasCapacity()); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From 023e207f139388198685b2e916c9d7cf558c79a7 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Thu, 26 Aug 2010 18:42:02 -0700 Subject: [PATCH 014/225] Fix problem with crash_sender being able to run multiple times concurrently. BUG=6134 TEST=Run CrashSender and UserCrash, also test starting those tests while crash_sender is already running. Change-Id: Ie80c450f6b0da1b67398aa67fd9b74903abc430b Review URL: http://codereview.chromium.org/3197024 --- crash_reporter/crash_sender | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 5ada4ef17..d265e18a7 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -9,6 +9,11 @@ set -e # Product ID in crash report CHROMEOS_PRODUCT=ChromeOS +# Should remove the run file when this process finishes. We don't want +# to always remove it since it may be for pre-existing crash_sender +# process. +CLEAN_UP_RUN_FILE=0 + # File whose existence implies crash reports may be sent, and whose # contents includes our machine's anonymized guid. CONSENT_ID="/home/chronos/Consent To Send Stats" @@ -46,26 +51,36 @@ TAG="$(basename $0)[$$]" # hours. TIMESTAMPS_DIR="/var/lib/crash_sender" +# Temp directory for this process. +TMP_DIR="" + lecho() { logger -t "${TAG}" "$@" } -cleanup_tmp_dir() { - rm -rf "${TMP_DIR}" -} - -cleanup_run_file_and_tmp_dir() { - rm -f "${RUN_FILE}" - cleanup_tmp_dir +cleanup() { + if [ -n "${TMP_DIR}" ]; then + rm -rf "${TMP_DIR}" + fi + if [ ${CLEAN_UP_RUN_FILE} -eq 1 ]; then + rm -f "${RUN_FILE}" + fi } check_not_already_running() { - if [ ! -f "${RUN_FILE}" ]; then + set -o noclobber + if echo $$ 2>/dev/null > "${RUN_FILE}"; then + # Able to write RUN_FILE without contention. + CLEAN_UP_RUN_FILE=1 + set +o noclobber return fi + set +o noclobber local last_pid=$(cat "${RUN_FILE}") if [ ! -f "/proc/${last_pid}/cmdline" ]; then - trap cleanup_run_file_and_tmp_dir EXIT INT + CLEAN_UP_RUN_FILE=1 + # Note that this write may be executed by two crash_senders who + # simulataneously reap the existing dangling run file echo $$ > "${RUN_FILE}" return fi @@ -233,6 +248,7 @@ send_crashes() { } main() { + trap cleanup EXIT INT TERM if [ -e "${PAUSE_CRASH_SENDING}" ]; then lecho "Exiting early due to ${PAUSE_CRASH_SENDING}" exit 1 @@ -246,7 +262,6 @@ main() { fi TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXX)" - trap cleanup_tmp_dir EXIT INT # Send system-wide crashes send_crashes "/var/spool/crash" From f174fc08dca7201b493be4187c450d925e113689 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 8 Sep 2010 20:55:26 -0700 Subject: [PATCH 015/225] Fix collection of kernel crash into /var/spool/crash No kcrash was collected because the IsEnabled() test always failed: we must do the kernel_collector->Enable() first. I've not checked if this might break the Override path (for local testing): it's more important for the feature to work for real than in an artificial test. BUG=837,5865 TEST=logging_KernelCrashServer or manual procedure Signed-off-by: Hugh Dickins Review URL: http://codereview.chromium.org/3317007 --- crash_reporter/crash_reporter.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index e850e3070..37c62a6fb 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -89,6 +89,7 @@ static int Initialize(KernelCollector *kernel_collector, bool was_kernel_crash = false; bool was_unclean_shutdown = false; + kernel_collector->Enable(); if (kernel_collector->IsEnabled()) { was_kernel_crash = kernel_collector->Collect(); } @@ -111,7 +112,6 @@ static int Initialize(KernelCollector *kernel_collector, } // Must enable the unclean shutdown collector *after* collecting. - kernel_collector->Enable(); unclean_shutdown_collector->Enable(); user_collector->Enable(); From 6fba658f1fbd3e97cb50b2ef872247ef43989321 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 8 Sep 2010 20:59:58 -0700 Subject: [PATCH 016/225] Fix Unexpected crash_sender stdout/stderr when testing Official image logging_KernelCrashServer and logging_UserCrash tests fail on an Official image, because crash_sender's is_official() test leaks the "Official" line: grep needs the quiet option (and does not need a cut). BUG=837 TEST=logging_UserCrash Signed-off-by: Hugh Dickins Review URL: http://codereview.chromium.org/3357010 --- crash_reporter/crash_sender | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index d265e18a7..37c3362bf 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -94,8 +94,7 @@ get_version() { } is_official() { - grep ^CHROMEOS_RELEASE_DESCRIPTION /etc/lsb-release | cut -d = -f 2- | \ - grep Official + grep ^CHROMEOS_RELEASE_DESCRIPTION /etc/lsb-release | grep -q Official } # Generate a uniform random number in 0..max-1. From da5db7a8304e22af6c57733460516857e9211b32 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Fri, 17 Sep 2010 13:50:42 -0700 Subject: [PATCH 017/225] Increase number of enqueued crash files, sending rate, and fix minor bug. Up to 32 crashes per spool and 32 sends per day. BUG=6355,6782 Change-Id: I99e86265c035ad62937aa135467681be8ef70f9a TEST=UserCrash and CrashSender Review URL: http://codereview.chromium.org/3414006 --- crash_reporter/crash_collector.cc | 11 +++++++++-- crash_reporter/crash_sender | 9 ++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 0194fc41c..f2f514694 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -26,8 +26,15 @@ static const mode_t kSystemCrashPathMode = 01755; static const uid_t kRootOwner = 0; static const uid_t kRootGroup = 0; -// Maximum of 8 crash reports per directory. -const int CrashCollector::kMaxCrashDirectorySize = 8; +// Maximum crash reports per crash spool directory. Note that this is +// a separate maximum from the maximum rate at which we upload these +// diagnostics. The higher this rate is, the more space we allow for +// core files, minidumps, and kcrash logs, and equivalently the more +// processor and I/O bandwidth we dedicate to handling these crashes when +// many occur at once. Also note that if core files are configured to +// be left on the file system, we stop adding crashes when either the +// number of core files or minidumps reaches this number. +const int CrashCollector::kMaxCrashDirectorySize = 32; CrashCollector::CrashCollector() : forced_crash_directory_(NULL) { } diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 37c3362bf..c62f88aa4 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -21,8 +21,8 @@ CONSENT_ID="/home/chronos/Consent To Send Stats" # Path to find which is required for computing the crash rate. FIND="/usr/bin/find" -# Send up to 8 crashes per day. -MAX_CRASH_RATE=8 +# Maximum crashes to send per day. +MAX_CRASH_RATE=32 # File whose existence mocks crash sending. If empty we pretend the # crash sending was successful, otherwise unsuccessful. @@ -197,13 +197,12 @@ send_crash() { lecho "Crash sending failed with: $(cat ${curl_stderr})" fi - rm -f "${report_id}" "${output_file}" + rm -f "${report_id}" return ${curl_result} } -# Send all crashes from the given directory. The directory is currently -# expected to just contain a bunch of minidumps. +# Send all crashes from the given directory. send_crashes() { local dir="$1" lecho "Considering crashes in ${dir}" From 9dc2fbf616cd068dd0c6c7617bcb53132fca8dec Mon Sep 17 00:00:00 2001 From: ChromeOS Developer Date: Tue, 21 Sep 2010 14:05:23 -0700 Subject: [PATCH 018/225] Add call to logging::InitLogging() during startup to write to stderr. There's no need to write to a file since we only log messages for commandline parameter validations (ie. usage errors). BUG=6446 TEST=unit test Change-Id: I0e55d569534f953526a6b788dbc6b3e284ff8b4f Review URL: http://codereview.chromium.org/3464008 --- crash_reporter/crash_reporter.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 37c62a6fb..ff99748b2 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -143,6 +143,10 @@ int main(int argc, char *argv[]) { FilePath my_path(argv[0]); file_util::AbsolutePath(&my_path); s_metrics_lib.Init(); + InitLogging(NULL, + LOG_ONLY_TO_SYSTEM_DEBUG_LOG, + DONT_LOCK_LOG_FILE, + DELETE_OLD_LOG_FILE); s_system_log.Initialize(my_path.BaseName().value().c_str()); KernelCollector kernel_collector; kernel_collector.Initialize(CountKernelCrash, From cd71405362a28b34e6d75734adcdb84796a082c6 Mon Sep 17 00:00:00 2001 From: Kenneth Waters Date: Tue, 21 Sep 2010 16:08:54 -0700 Subject: [PATCH 019/225] Revert "Add call to logging::InitLogging() during startup to write to stderr." This reverts commit 93cb6e57175e8e622c8f417bfad0213b125b57db. TEST=none BUG=none Review URL: http://codereview.chromium.org/3390021 Change-Id: I979ddb3ab2542968e1f9a521344389bc7edf8492 --- crash_reporter/crash_reporter.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index ff99748b2..37c62a6fb 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -143,10 +143,6 @@ int main(int argc, char *argv[]) { FilePath my_path(argv[0]); file_util::AbsolutePath(&my_path); s_metrics_lib.Init(); - InitLogging(NULL, - LOG_ONLY_TO_SYSTEM_DEBUG_LOG, - DONT_LOCK_LOG_FILE, - DELETE_OLD_LOG_FILE); s_system_log.Initialize(my_path.BaseName().value().c_str()); KernelCollector kernel_collector; kernel_collector.Initialize(CountKernelCrash, From 563d08ba7db62d8d2cc85dc9275d2ee883d47c25 Mon Sep 17 00:00:00 2001 From: Thieu Le Date: Tue, 21 Sep 2010 17:07:05 -0700 Subject: [PATCH 020/225] Fix build break. BUG=6446 TEST=unit test, logging_UserCrash Change-Id: Iac9f5484d9218ef91ae606ef410409df5ec16382 Review URL: http://codereview.chromium.org/3380022 --- crash_reporter/crash_reporter.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 37c62a6fb..b57bfc40b 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -5,6 +5,7 @@ #include #include "base/file_util.h" +#include "base/command_line.h" #include "base/logging.h" #include "base/string_util.h" #include "crash-reporter/kernel_collector.h" @@ -143,6 +144,11 @@ int main(int argc, char *argv[]) { FilePath my_path(argv[0]); file_util::AbsolutePath(&my_path); s_metrics_lib.Init(); + CommandLine::Init(argc, argv); + logging::InitLogging(NULL, + logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG, + logging::DONT_LOCK_LOG_FILE, + logging::DELETE_OLD_LOG_FILE); s_system_log.Initialize(my_path.BaseName().value().c_str()); KernelCollector kernel_collector; kernel_collector.Initialize(CountKernelCrash, From ee849c5ef4c416cb0321483fa10c92f8aada3179 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Thu, 30 Sep 2010 15:30:10 -0700 Subject: [PATCH 021/225] Add meta files to crash directory, enabling OS version at crash time. Adding meta files also: 1) ensures atomically added crash reports 2) allows us to remove orphaned crash report payload files (such as core files) 3) gives us better control over the number of reports in crash directory While we're here, also made these minor changes 1) send board (x86-generic, x86-mario, etc) to crash server 2) send hwclass to crash server 3) Only record crash reports when metrics are enabled. 4) No longer allow crash reporting to staging server. BUG=6100,5805,5624,6865 TEST=unit tests plus UserCrash,CrashSender,KernelCrash autotests Change-Id: Ieea9bdc8e0680b379c65b91cc56ca0611dd0f31c Review URL: http://codereview.chromium.org/3436029 --- crash_reporter/crash_collector.cc | 88 ++++++++-- crash_reporter/crash_collector.h | 25 ++- crash_reporter/crash_collector_test.cc | 103 +++++++++--- crash_reporter/crash_reporter.cc | 4 +- crash_reporter/crash_sender | 207 +++++++++++++++++------- crash_reporter/kernel_collector.cc | 27 ++-- crash_reporter/kernel_collector.h | 3 - crash_reporter/kernel_collector_test.cc | 15 -- crash_reporter/user_collector.cc | 13 +- 9 files changed, 359 insertions(+), 126 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index f2f514694..e91f70a12 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -8,12 +8,15 @@ #include // For struct passwd. #include // for mode_t. +#include + #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" #include "crash-reporter/system_logging.h" static const char kDefaultUserName[] = "chronos"; +static const char kLsbRelease[] = "/etc/lsb-release"; static const char kSystemCrashPath[] = "/var/spool/crash"; static const char kUserCrashPath[] = "/home/chronos/user/crash"; @@ -55,13 +58,23 @@ void CrashCollector::Initialize( logger_ = logger; } +std::string CrashCollector::Sanitize(const std::string &name) { + std::string result = name; + for (size_t i = 0; i < name.size(); ++i) { + if (!isalnum(result[i]) && result[i] != '_') + result[i] = '_'; + } + return result; +} + std::string CrashCollector::FormatDumpBasename(const std::string &exec_name, time_t timestamp, pid_t pid) { struct tm tm; localtime_r(×tamp, &tm); + std::string sanitized_exec_name = Sanitize(exec_name); return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d", - exec_name.c_str(), + sanitized_exec_name.c_str(), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, @@ -173,22 +186,27 @@ bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) { } struct dirent ent_buf; struct dirent* ent; - int count_non_core = 0; - int count_core = 0; bool full = false; + std::set basenames; while (readdir_r(dir, &ent_buf, &ent) == 0 && ent != NULL) { if ((strcmp(ent->d_name, ".") == 0) || (strcmp(ent->d_name, "..") == 0)) continue; - if (strcmp(ent->d_name + strlen(ent->d_name) - 5, ".core") == 0) { - ++count_core; - } else { - ++count_non_core; - } + std::string filename(ent->d_name); + size_t last_dot = filename.rfind("."); + std::string basename; + // If there is a valid looking extension, use the base part of the + // name. If the only dot is the first byte (aka a dot file), treat + // it as unique to avoid allowing a directory full of dot files + // from accumulating. + if (last_dot != std::string::npos && last_dot != 0) + basename = filename.substr(0, last_dot); + else + basename = filename; + basenames.insert(basename); - if (count_core >= kMaxCrashDirectorySize || - count_non_core >= kMaxCrashDirectorySize) { + if (basenames.size() >= static_cast(kMaxCrashDirectorySize)) { logger_->LogWarning( "Crash directory %s already full with %d pending reports", crash_directory.value().c_str(), @@ -200,3 +218,53 @@ bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) { closedir(dir); return !full; } + +bool CrashCollector::ReadKeyValueFile( + const FilePath &path, + const char separator, + std::map *dictionary) { + std::string contents; + if (!file_util::ReadFileToString(path, &contents)) { + return false; + } + typedef std::vector StringVector; + StringVector lines; + SplitString(contents, '\n', &lines); + bool any_errors = false; + for (StringVector::iterator line = lines.begin(); line != lines.end(); + ++line) { + // Allow empty strings. + if (line->empty()) + continue; + StringVector sides; + SplitString(*line, separator, &sides); + if (sides.size() != 2) { + any_errors = true; + continue; + } + dictionary->insert(std::pair(sides[0], sides[1])); + } + return !any_errors; +} + +void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, + const std::string &exec_name) { + std::map contents; + if (!ReadKeyValueFile(FilePath(std::string(kLsbRelease)), '=', &contents)) { + logger_->LogError("Problem parsing %s", kLsbRelease); + // Even though there was some failure, take as much as we could read. + } + std::string version("unknown"); + std::map::iterator i; + if ((i = contents.find("CHROMEOS_RELEASE_VERSION")) != contents.end()) { + version = i->second; + } + std::string meta_data = StringPrintf("exec_name=%s\n" + "ver=%s\n" + "done=1\n", + exec_name.c_str(), + version.c_str()); + if (!file_util::WriteFile(meta_path, meta_data.c_str(), meta_data.size())) { + logger_->LogError("Unable to write %s", meta_path.value().c_str()); + } +} diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index a75d79148..730a8e761 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -5,9 +5,11 @@ #ifndef _CRASH_REPORTER_CRASH_COLLECTOR_H_ #define _CRASH_REPORTER_CRASH_COLLECTOR_H_ -#include #include +#include +#include + #include "gtest/gtest_prod.h" // for FRIEND_TEST class FilePath; @@ -32,15 +34,22 @@ class CrashCollector { protected: friend class CrashCollectorTest; - FRIEND_TEST(CrashCollectorTest, CheckHasCapacityOverCore); - FRIEND_TEST(CrashCollectorTest, CheckHasCapacityOverNonCore); + FRIEND_TEST(CrashCollectorTest, CheckHasCapacityCorrectBasename); + FRIEND_TEST(CrashCollectorTest, CheckHasCapacityStrangeNames); + FRIEND_TEST(CrashCollectorTest, CheckHasCapacityUsual); FRIEND_TEST(CrashCollectorTest, GetCrashDirectoryInfo); FRIEND_TEST(CrashCollectorTest, FormatDumpBasename); FRIEND_TEST(CrashCollectorTest, Initialize); + FRIEND_TEST(CrashCollectorTest, ReadKeyValueFile); + FRIEND_TEST(CrashCollectorTest, Sanitize); // Set maximum enqueued crashes in a crash directory. static const int kMaxCrashDirectorySize; + // Return a filename that has only [a-z0-1_] characters by mapping + // all others into '_'. + std::string Sanitize(const std::string &name); + // For testing, set the directory always returned by // GetCreatedCrashDirectoryByEuid. void ForceCrashDirectory(const char *forced_directory) { @@ -72,6 +81,16 @@ class CrashCollector { // crash. bool CheckHasCapacity(const FilePath &crash_directory); + // Read the given file of form [\n...] and return + // a map of its contents. + bool ReadKeyValueFile(const FilePath &file, + char separator, + std::map *dictionary); + + // Write a file of metadata about crash. + void WriteCrashMetaData(const FilePath &meta_path, + const std::string &exec_name); + CountCrashFunction count_crash_function_; IsFeedbackAllowedFunction is_feedback_allowed_function_; SystemLogging *logger_; diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index ed8fff756..2dbf2bb2d 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -48,6 +48,16 @@ TEST_F(CrashCollectorTest, Initialize) { ASSERT_TRUE(&logging_ == collector_.logger_); } +TEST_F(CrashCollectorTest, Sanitize) { + EXPECT_EQ("chrome", collector_.Sanitize("chrome")); + EXPECT_EQ("CHROME", collector_.Sanitize("CHROME")); + EXPECT_EQ("1chrome2", collector_.Sanitize("1chrome2")); + EXPECT_EQ("chrome__deleted_", collector_.Sanitize("chrome (deleted)")); + EXPECT_EQ("foo_bar", collector_.Sanitize("foo.bar")); + EXPECT_EQ("", collector_.Sanitize("")); + EXPECT_EQ("_", collector_.Sanitize(" ")); +} + TEST_F(CrashCollectorTest, GetCrashDirectoryInfo) { FilePath path; const int kRootUid = 0; @@ -118,41 +128,96 @@ bool CrashCollectorTest::CheckHasCapacity() { return has_capacity; } -TEST_F(CrashCollectorTest, CheckHasCapacityOverNonCore) { - // Test up to kMaxCrashDirectorySize-1 non-core files can be added. +TEST_F(CrashCollectorTest, CheckHasCapacityUsual) { + // Test kMaxCrashDirectorySize - 1 non-meta files can be added. for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { - EXPECT_TRUE(CheckHasCapacity()); - file_util::WriteFile(test_dir_.Append(StringPrintf("file%d", i)), "", 0); - } - - // Test an additional kMaxCrashDirectorySize - 1 core files fit. - for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { - EXPECT_TRUE(CheckHasCapacity()); file_util::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)), "", 0); + EXPECT_TRUE(CheckHasCapacity()); } - // Test an additional kMaxCrashDirectorySize non-core files don't fit. + // Test an additional kMaxCrashDirectorySize - 1 meta files fit. + for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { + file_util::WriteFile(test_dir_.Append(StringPrintf("file%d.meta", i)), + "", 0); + EXPECT_TRUE(CheckHasCapacity()); + } + + // Test an additional kMaxCrashDirectorySize meta files don't fit. for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) { - file_util::WriteFile(test_dir_.Append(StringPrintf("overage%d", i)), "", 0); + file_util::WriteFile(test_dir_.Append(StringPrintf("overage%d.meta", i)), + "", 0); EXPECT_FALSE(CheckHasCapacity()); } } -TEST_F(CrashCollectorTest, CheckHasCapacityOverCore) { - // Set up kMaxCrashDirectorySize - 1 core files. +TEST_F(CrashCollectorTest, CheckHasCapacityCorrectBasename) { + // Test kMaxCrashDirectorySize - 1 files can be added. for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { - file_util::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)), + file_util::WriteFile(test_dir_.Append(StringPrintf("file.%d.core", i)), "", 0); + EXPECT_TRUE(CheckHasCapacity()); } - - EXPECT_TRUE(CheckHasCapacity()); - - // Test an additional core file does not fit. - file_util::WriteFile(test_dir_.Append("overage.core"), "", 0); + file_util::WriteFile(test_dir_.Append("file.last.core"), "", 0); EXPECT_FALSE(CheckHasCapacity()); } +TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) { + // Test many files with different extensions and same base fit. + for (int i = 0; i < 5 * CrashCollector::kMaxCrashDirectorySize; ++i) { + file_util::WriteFile(test_dir_.Append(StringPrintf("a.%d", i)), "", 0); + EXPECT_TRUE(CheckHasCapacity()); + } + // Test dot files are treated as individual files. + for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 2; ++i) { + file_util::WriteFile(test_dir_.Append(StringPrintf(".file%d", i)), "", 0); + EXPECT_TRUE(CheckHasCapacity()); + } + file_util::WriteFile(test_dir_.Append("normal.meta"), "", 0); + EXPECT_FALSE(CheckHasCapacity()); +} + +TEST_F(CrashCollectorTest, ReadKeyValueFile) { + const char *contents = ("a=b\n" + "\n" + " c=d \n"); + FilePath path(test_dir_.Append("keyval")); + std::map dictionary; + std::map::iterator i; + + file_util::WriteFile(path, contents, strlen(contents)); + + EXPECT_TRUE(collector_.ReadKeyValueFile(path, '=', &dictionary)); + i = dictionary.find("a"); + EXPECT_TRUE(i != dictionary.end() && i->second == "b"); + i = dictionary.find("c"); + EXPECT_TRUE(i != dictionary.end() && i->second == "d"); + + dictionary.clear(); + + contents = ("a=b c d\n" + "e\n" + " f g = h\n" + "i=j\n" + "=k\n" + "l=\n"); + file_util::WriteFile(path, contents, strlen(contents)); + + EXPECT_FALSE(collector_.ReadKeyValueFile(path, '=', &dictionary)); + i = dictionary.find("a"); + EXPECT_TRUE(i != dictionary.end() && i->second == "b c d"); + i = dictionary.find("e"); + EXPECT_TRUE(i == dictionary.end()); + i = dictionary.find("f g"); + EXPECT_TRUE(i != dictionary.end() && i->second == "h"); + i = dictionary.find("i"); + EXPECT_TRUE(i != dictionary.end() && i->second == "j"); + i = dictionary.find(""); + EXPECT_TRUE(i != dictionary.end() && i->second == "k"); + i = dictionary.find("l"); + EXPECT_TRUE(i != dictionary.end() && i->second == ""); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index b57bfc40b..d85838698 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -43,9 +43,7 @@ static MetricsLibrary s_metrics_lib; static SystemLoggingImpl s_system_log; static bool IsFeedbackAllowed() { - // Once crosbug.com/5814 is fixed, call the is opted in function - // here. - return true; + return s_metrics_lib.AreMetricsEnabled(); } static bool TouchFile(const FilePath &file_path) { diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index c62f88aa4..f6b3e3a58 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -21,8 +21,15 @@ CONSENT_ID="/home/chronos/Consent To Send Stats" # Path to find which is required for computing the crash rate. FIND="/usr/bin/find" +# Set this to 1 in the environment to allow uploading crash reports +# for unofficial versions. +FORCE_OFFICIAL=${FORCE_OFFICIAL:-0} + +# Path to hardware class description. +HWCLASS_PATH="/sys/devices/platform/chromeos_acpi/HWID" + # Maximum crashes to send per day. -MAX_CRASH_RATE=32 +MAX_CRASH_RATE=${MAX_CRASH_RATE:-32} # File whose existence mocks crash sending. If empty we pretend the # crash sending was successful, otherwise unsuccessful. @@ -32,9 +39,6 @@ MOCK_CRASH_SENDING="/tmp/mock-crash-sending" # Must be stateful to enable testing kernel crashes. PAUSE_CRASH_SENDING="/var/lib/crash_sender_paused" -# URL to send non-official build crash reports to. -REPORT_UPLOAD_STAGING_URL="http://clients2.google.com/cr/staging_report" - # URL to send official build crash reports to. REPORT_UPLOAD_PROD_URL="http://clients2.google.com/cr/report" @@ -89,11 +93,8 @@ check_not_already_running() { exit 1 } -get_version() { - grep ^CHROMEOS_RELEASE_VERSION /etc/lsb-release | cut -d = -f 2- -} - is_official() { + [ ${FORCE_OFFICIAL} -ne 0 ] && return 0 grep ^CHROMEOS_RELEASE_DESCRIPTION /etc/lsb-release | grep -q Official } @@ -119,7 +120,8 @@ is_on_3g() { check_rate() { mkdir -p ${TIMESTAMPS_DIR} # Only consider minidumps written in the past 24 hours by removing all older. - ${FIND} "${TIMESTAMPS_DIR}" -mindepth 1 -mmin +$((24 * 60)) -exec rm '{}' ';' + ${FIND} "${TIMESTAMPS_DIR}" -mindepth 1 -mmin +$((24 * 60)) \ + -exec rm -- '{}' ';' local sends_in_24hrs=$(echo "${TIMESTAMPS_DIR}"/* | wc -w) lecho "Current send rate: ${sends_in_24hrs}sends/24hrs" if [ ${sends_in_24hrs} -ge ${MAX_CRASH_RATE} ]; then @@ -132,37 +134,81 @@ check_rate() { return 0 } -# Return if $1 is a .core file -get_kind() { - local kind="${1##*.}" - if [ "${kind}" = "dmp" ]; then - kind="minidump" - fi - echo ${kind} +# Gets the base part of a crash report file, such as +# name.01234.5678.9012 from name.01234.5678.9012.meta +get_base() { + echo "${1%.*}" } -get_exec_name() { - local filename=$(basename "$1") - echo "${filename%%.*}" +# Return which kind of report the given metadata file relates to +get_kind() { + # There should never be a report with both a dmp and kcrash file. + # If that were to happen we arbitrarily consider this a minidump + # report and effectively ignore the kcrash. + local base="$(get_base "$1")" + if [ -r "${base}.dmp" ]; then + echo "minidump" + return + fi + if [ -r "${base}.kcrash" ]; then + echo "kcrash" + return + fi +} + +get_key_value() { + if ! grep -q "$2=" "$1"; then + echo "undefined" + return + fi + grep "$2=" "$1" | cut -d = -f 2- +} + +# Returns true if mock is enabled. +is_mock() { + [ -f "${MOCK_CRASH_SENDING}" ] && return 0 + return 1 +} + +# Return the board name. +get_board() { + echo $(get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_BOARD") +} + +# Return the hardware class or "unknown". +get_hardware_class() { + if [ -r "${HWCLASS_PATH}" ]; then + cat "${HWCLASS_PATH}" + else + echo "unknown" + fi } send_crash() { - local report_path="$1" - local kind=$(get_kind "${report_path}") - local exec_name=$(get_exec_name "${report_path}") + local meta_path="$1" + local kind="$(get_kind "${meta_path}")" + local exec_name="$(get_key_value "${meta_path}" "exec_name")" local sleep_time=$(generate_uniform_random $SECONDS_SEND_SPREAD) - local url="${REPORT_UPLOAD_STAGING_URL}" - if is_official; then - url="${REPORT_UPLOAD_PROD_URL}" - fi + local url="${REPORT_UPLOAD_PROD_URL}" + local chromeos_version="$(get_key_value "${meta_path}" "ver")" + local board="$(get_board)" + local hwclass="$(get_hardware_class)" + local payload_extension="${kind}" + [ "${kind}" = "minidump" ] && payload_extension="dmp" + local report_payload="$(get_base "${meta_path}").${payload_extension}" lecho "Sending crash:" lecho " Scheduled to send in ${sleep_time}s" - lecho " Report: ${report_path} (${kind})" - lecho " URL: ${url}" - lecho " Product: ${CHROMEOS_PRODUCT}" + lecho " Metadata: ${meta_path} (${kind})" + lecho " Payload: ${report_payload}" lecho " Version: ${chromeos_version}" + if is_mock; then + lecho " Product: ${CHROMEOS_PRODUCT}" + lecho " URL: ${url}" + lecho " Board: ${board}" + lecho " HWClass: ${hwclass}" + fi lecho " Exec name: ${exec_name}" - if [ -f "${MOCK_CRASH_SENDING}" ]; then + if is_mock; then local mock_in=$(cat "${MOCK_CRASH_SENDING}") if [ "${mock_in}" = "" ]; then lecho "Mocking successful send" @@ -185,7 +231,9 @@ send_crash() { curl "${url}" \ -F "prod=${CHROMEOS_PRODUCT}" \ -F "ver=${chromeos_version}" \ - -F "upload_file_${kind}=@${report_path}" \ + -F "upload_file_${kind}=@${report_payload}" \ + -F "board=${board}" \ + -F "hwclass=${hwclass}" \ -F "exec_name=${exec_name}" \ -F "guid=<${CONSENT_ID}" -o "${report_id}" 2>"${curl_stderr}" curl_result=$? @@ -202,53 +250,98 @@ send_crash() { return ${curl_result} } +# *.meta files always end with done=1 so we can tell if they are complete. +is_complete_metadata() { + grep -q "done=1" "$1" +} + +# Remove the given report path. +remove_report() { + local base="${1%.*}" + rm -f -- "${base}".* +} + # Send all crashes from the given directory. send_crashes() { local dir="$1" - lecho "Considering crashes in ${dir}" # Cycle through minidumps, most recent first. That way if we're about # to exceed the daily rate, we send the most recent minidumps. if [ ! -d "${dir}" ]; then return fi - for file in $(ls -1t "${dir}"); do - local report_path="${dir}/${file}" - lecho "Considering file ${report_path}" - local kind=$(get_kind "${report_path}") - if [ "${kind}" = "core" ]; then - lecho "Ignoring core file." - continue - elif [ "${kind}" != "minidump" ] && [ "${kind}" != "kcrash" ]; then - lecho "Unknown report kind: ${kind}. Removing report." - rm -f "${report_path}" + # Consider any old files which still have no corresponding meta file + # as orphaned, and remove them. + for old_file in $(${FIND} "${dir}" -mindepth 1 \ + -mmin +$((24 * 60)) -type f); do + if [ ! -e "$(get_base "${old_file}").meta" ]; then + lecho "Removing old orphaned file: ${old_file}." + rm -f -- "${old_file}" + fi + done + + # Look through all metadata (*.meta) files, if any exist. + for meta_path in $(ls -1t "${dir}"/*.meta 2>/dev/null); do + lecho "Considering metadata ${meta_path}." + local kind=$(get_kind "${meta_path}") + + if [ "${kind}" != "minidump" ] && [ "${kind}" != "kcrash" ]; then + lecho "Unknown report kind. Removing report." + remove_report "${meta_path}" continue fi - if ! check_rate; then - lecho "Sending ${report_path} would exceed rate. Leaving for later." - return 0 - fi - local chromeos_version=$(get_version) + if is_feedback_disabled; then lecho "Uploading is disabled. Removing crash." - rm "${report_path}" - elif is_on_3g; then - lecho "Not sending crash report while on 3G, saving for later." - elif send_crash "${report_path}"; then - # Send was successful, now remove - lecho "Successfully sent crash ${report_path} and removing" - rm "${report_path}" - else - lecho "Problem sending ${report_path}, not removing" + remove_report "${meta_path}" + continue fi + + if ! is_mock && ! is_official; then + lecho "Not an official OS version. Removing crash." + remove_report "${meta_path}" + continue + fi + + if is_on_3g; then + lecho "Not sending crash reports while on 3G, saving for later." + return 0 + fi + + if ! is_complete_metadata "${meta_path}"; then + # This report is incomplete, so if it's old, just remove it. + local old_meta=$(${FIND} "${dir}" -mindepth 1 -name \ + $(basename "${meta_path}") -mmin +$((24 * 60)) -type f) + if [ -n "${old_meta}" ]; then + lecho "Removing old incomplete metadata." + remove_report "${meta_path}" + else + lecho "Ignoring recent incomplete metadata." + fi + continue + fi + + if ! check_rate; then + lecho "Sending ${meta_path} would exceed rate. Leaving for later." + return 0 + fi + + if ! send_crash "${meta_path}"; then + lecho "Problem sending ${meta_path}, not removing." + continue + fi + + # Send was successful, now remove. + lecho "Successfully sent crash ${meta_path} and removing." + remove_report "${meta_path}" done } main() { trap cleanup EXIT INT TERM if [ -e "${PAUSE_CRASH_SENDING}" ]; then - lecho "Exiting early due to ${PAUSE_CRASH_SENDING}" + lecho "Exiting early due to ${PAUSE_CRASH_SENDING}." exit 1 fi diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 1a79c9d86..f5e20b901 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -66,17 +66,6 @@ bool KernelCollector::ClearPreservedDump() { return true; } -FilePath KernelCollector::GetKernelCrashPath( - const FilePath &root_crash_path, - time_t timestamp) { - std::string dump_basename = - FormatDumpBasename(kKernelExecName, - timestamp, - kKernelPid); - return root_crash_path.Append( - StringPrintf("%s.kcrash", dump_basename.c_str())); -} - bool KernelCollector::Collect() { std::string kernel_dump; FilePath root_crash_directory; @@ -86,6 +75,8 @@ bool KernelCollector::Collect() { if (kernel_dump.empty()) { return false; } + logger_->LogInfo("Received prior crash notification from kernel"); + if (is_feedback_allowed_function_()) { count_crash_function_(); @@ -94,8 +85,13 @@ bool KernelCollector::Collect() { return true; } - FilePath kernel_crash_path = GetKernelCrashPath(root_crash_directory, - time(NULL)); + std::string dump_basename = + FormatDumpBasename(kKernelExecName, + time(NULL), + kKernelPid); + FilePath kernel_crash_path = root_crash_directory.Append( + StringPrintf("%s.kcrash", dump_basename.c_str())); + if (file_util::WriteFile(kernel_crash_path, kernel_dump.data(), kernel_dump.length()) != @@ -105,6 +101,11 @@ bool KernelCollector::Collect() { return true; } + WriteCrashMetaData( + root_crash_directory.Append( + StringPrintf("%s.meta", dump_basename.c_str())), + kKernelExecName); + logger_->LogInfo("Collected kernel crash diagnostics into %s", kernel_crash_path.value().c_str()); } else { diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index 3e2944245..e6cd57392 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -37,14 +37,11 @@ class KernelCollector : public CrashCollector { private: friend class KernelCollectorTest; FRIEND_TEST(KernelCollectorTest, ClearPreservedDump); - FRIEND_TEST(KernelCollectorTest, GetKernelCrashPath); FRIEND_TEST(KernelCollectorTest, LoadPreservedDump); FRIEND_TEST(KernelCollectorTest, CollectOK); bool LoadPreservedDump(std::string *contents); bool ClearPreservedDump(); - FilePath GetKernelCrashPath(const FilePath &root_crash_path, - time_t timestamp); bool is_enabled_; FilePath preserved_dump_path_; diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 7188e2f15..44a5ba0a7 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -95,21 +95,6 @@ TEST_F(KernelCollectorTest, ClearPreservedDump) { ASSERT_EQ(KernelCollector::kClearingSequence, dump); } -TEST_F(KernelCollectorTest, GetKernelCrashPath) { - FilePath root("/var/spool/crash"); - struct tm tm = {0}; - tm.tm_sec = 15; - tm.tm_min = 50; - tm.tm_hour = 13; - tm.tm_mday = 23; - tm.tm_mon = 4; - tm.tm_year = 110; - tm.tm_isdst = -1; - FilePath path = collector_.GetKernelCrashPath(root, mktime(&tm)); - ASSERT_EQ("/var/spool/crash/kernel.20100523.135015.0.kcrash", - path.value()); -} - TEST_F(KernelCollectorTest, CollectPreservedFileMissing) { ASSERT_FALSE(collector_.Collect()); ASSERT_NE(logging_.log().find("Unable to read test/kcrash"), diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index e051a4ac3..18ab7b95d 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -275,6 +275,11 @@ bool UserCollector::GenerateDiagnostics(pid_t pid, conversion_result = false; } + WriteCrashMetaData( + crash_path.Append( + StringPrintf("%s.meta", dump_basename.c_str())), + exec_name); + if (conversion_result) { logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str()); } @@ -298,10 +303,12 @@ bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { // failing by indicating an unknown name. exec = "unknown"; } - logger_->LogWarning("Received crash notification for %s[%d] sig %d", - exec.c_str(), pid, signal); + bool feedback = is_feedback_allowed_function_(); + logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", + exec.c_str(), pid, signal, + feedback ? "handling" : "ignoring"); - if (is_feedback_allowed_function_()) { + if (feedback) { count_crash_function_(); if (generate_diagnostics_) { From 44973b02912e0858edb001cb5f8b8ba8da602e23 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Fri, 1 Oct 2010 15:40:11 -0700 Subject: [PATCH 022/225] crash-reporter: detect guest mode and defer crash sending Change-Id: I8952fc6e701521dbb8618889d96ebc025f713427 BUG=7203 TEST=Ran BVTs Review URL: http://codereview.chromium.org/3520011 --- crash_reporter/crash_sender | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index f6b3e3a58..c9eec0591 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -31,6 +31,9 @@ HWCLASS_PATH="/sys/devices/platform/chromeos_acpi/HWID" # Maximum crashes to send per day. MAX_CRASH_RATE=${MAX_CRASH_RATE:-32} +# Path to metrics_client. +METRICS_CLIENT="/usr/bin/metrics_client" + # File whose existence mocks crash sending. If empty we pretend the # crash sending was successful, otherwise unsuccessful. MOCK_CRASH_SENDING="/tmp/mock-crash-sending" @@ -105,11 +108,6 @@ generate_uniform_random() { echo $((random % max)) } -is_feedback_disabled() { - [ -r "${CONSENT_ID}" ] && return 1 - return 0 -} - is_on_3g() { # See crosbug.com/3304. return 1 @@ -292,7 +290,12 @@ send_crashes() { continue fi - if is_feedback_disabled; then + if ${METRICS_CLIENT} -g; then + lecho "Guest mode has been entered. Delaying crash sending until exited." + return 0 + fi + + if ! ${METRICS_CLIENT} -c; then lecho "Uploading is disabled. Removing crash." remove_report "${meta_path}" continue @@ -347,10 +350,12 @@ main() { check_not_already_running - if [ ! -x "${FIND}" ]; then - lecho "Fatal: Crash sending disabled: ${FIND} not found." - exit 1 - fi + for dependency in "${FIND}" "${METRICS_CLIENT}"; do + if [ ! -x "${dependency}" ]; then + lecho "Fatal: Crash sending disabled: ${dependency} not found." + exit 1 + fi + done TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXX)" From 30c976216bbd7232d1c83d5d9c4cb48b0cb0c14a Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Mon, 18 Oct 2010 12:09:31 -0700 Subject: [PATCH 023/225] crash-reporter: fix race condition between cron crash_sender and test invoked Change-Id: I274072faffcb49b1910352cbcc5005e3144d9ab0 BUG=7765 TEST=CrashSender and UserCrash Review URL: http://codereview.chromium.org/3748011 --- crash_reporter/crash_sender | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index c9eec0591..244c31d2a 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -28,6 +28,9 @@ FORCE_OFFICIAL=${FORCE_OFFICIAL:-0} # Path to hardware class description. HWCLASS_PATH="/sys/devices/platform/chromeos_acpi/HWID" +# Ignore PAUSE_CRASH_SENDING file if set. +OVERRIDE_PAUSE_SENDING=${OVERRIDE_PAUSE_SENDING:-0} + # Maximum crashes to send per day. MAX_CRASH_RATE=${MAX_CRASH_RATE:-32} @@ -343,7 +346,8 @@ send_crashes() { main() { trap cleanup EXIT INT TERM - if [ -e "${PAUSE_CRASH_SENDING}" ]; then + if [ -e "${PAUSE_CRASH_SENDING}" ] && \ + [ ${OVERRIDE_PAUSE_SENDING} -eq 0 ]; then lecho "Exiting early due to ${PAUSE_CRASH_SENDING}." exit 1 fi From c909b6944602bd6c1eb6d3c54ec41b1a4fc57b07 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Mon, 18 Oct 2010 12:26:05 -0700 Subject: [PATCH 024/225] crash-reporter: send payload sizes to help diagnose corruption Change-Id: Ia68990138ebcc3dd6d644fe30d0721dde41c567e BUG=6299 TEST=UserCrash, CrashSender, KernelCrash Review URL: http://codereview.chromium.org/3820004 --- crash_reporter/crash_collector.cc | 9 +++++++-- crash_reporter/crash_collector.h | 3 ++- crash_reporter/crash_sender | 4 ++++ crash_reporter/kernel_collector.cc | 3 ++- crash_reporter/user_collector.cc | 3 ++- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index e91f70a12..0391fa541 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -248,7 +248,8 @@ bool CrashCollector::ReadKeyValueFile( } void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, - const std::string &exec_name) { + const std::string &exec_name, + const std::string &payload_path) { std::map contents; if (!ReadKeyValueFile(FilePath(std::string(kLsbRelease)), '=', &contents)) { logger_->LogError("Problem parsing %s", kLsbRelease); @@ -259,11 +260,15 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, if ((i = contents.find("CHROMEOS_RELEASE_VERSION")) != contents.end()) { version = i->second; } + int64 payload_size = -1; + file_util::GetFileSize(FilePath(payload_path), &payload_size); std::string meta_data = StringPrintf("exec_name=%s\n" "ver=%s\n" + "payload_size=%lld\n" "done=1\n", exec_name.c_str(), - version.c_str()); + version.c_str(), + payload_size); if (!file_util::WriteFile(meta_path, meta_data.c_str(), meta_data.size())) { logger_->LogError("Unable to write %s", meta_path.value().c_str()); } diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 730a8e761..97bf0d6e5 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -89,7 +89,8 @@ class CrashCollector { // Write a file of metadata about crash. void WriteCrashMetaData(const FilePath &meta_path, - const std::string &exec_name); + const std::string &exec_name, + const std::string &payload_path); CountCrashFunction count_crash_function_; IsFeedbackAllowedFunction is_feedback_allowed_function_; diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 244c31d2a..684986468 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -195,8 +195,10 @@ send_crash() { local board="$(get_board)" local hwclass="$(get_hardware_class)" local payload_extension="${kind}" + local write_payload_size="$(get_key_value "${meta_path}" "payload_size")" [ "${kind}" = "minidump" ] && payload_extension="dmp" local report_payload="$(get_base "${meta_path}").${payload_extension}" + local send_payload_size="$(stat --printf=%s "${report_payload}")" lecho "Sending crash:" lecho " Scheduled to send in ${sleep_time}s" lecho " Metadata: ${meta_path} (${kind})" @@ -236,6 +238,8 @@ send_crash() { -F "board=${board}" \ -F "hwclass=${hwclass}" \ -F "exec_name=${exec_name}" \ + -F "write_payload_size=${write_payload_size}" \ + -F "send_payload_size=${send_payload_size}" \ -F "guid=<${CONSENT_ID}" -o "${report_id}" 2>"${curl_stderr}" curl_result=$? set -e diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index f5e20b901..c6bb33fca 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -104,7 +104,8 @@ bool KernelCollector::Collect() { WriteCrashMetaData( root_crash_directory.Append( StringPrintf("%s.meta", dump_basename.c_str())), - kKernelExecName); + kKernelExecName, + kernel_crash_path.value()); logger_->LogInfo("Collected kernel crash diagnostics into %s", kernel_crash_path.value().c_str()); diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 18ab7b95d..4047eb49c 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -278,7 +278,8 @@ bool UserCollector::GenerateDiagnostics(pid_t pid, WriteCrashMetaData( crash_path.Append( StringPrintf("%s.meta", dump_basename.c_str())), - exec_name); + exec_name, + minidump_path.value()); if (conversion_result) { logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str()); From 2953c3a87a7b201bbf08555129f0640ede1d0cdb Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Mon, 18 Oct 2010 14:42:20 -0700 Subject: [PATCH 025/225] crash-reporter: Avoid using system to invoke core2md Change-Id: I1fc0b4dd6e4e84cb999ba61fedd43fc74a8fa1ba BUG=6941 TEST=bvts Review URL: http://codereview.chromium.org/3755011 --- crash_reporter/system_logging_mock.h | 2 + crash_reporter/user_collector.cc | 83 +++++++++++++++++++++---- crash_reporter/user_collector.h | 6 ++ crash_reporter/user_collector_test.cc | 87 ++++++++++++++++++++++++--- 4 files changed, 158 insertions(+), 20 deletions(-) diff --git a/crash_reporter/system_logging_mock.h b/crash_reporter/system_logging_mock.h index 639fe4257..f4fb13343 100644 --- a/crash_reporter/system_logging_mock.h +++ b/crash_reporter/system_logging_mock.h @@ -18,6 +18,8 @@ class SystemLoggingMock : public SystemLogging { const std::string &log() { return log_; } + void clear() { log_.clear(); } + private: static std::string identity_; std::string log_; diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 4047eb49c..bc414ab7a 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -4,12 +4,17 @@ #include "crash-reporter/user_collector.h" +#include // For creat. #include // For struct group. #include // For struct passwd. -#include // For getpwuid_r and getgrnam_r. +#include // For getpwuid_r, getgrnam_r, WEXITSTATUS. +#include // For waitpid. +#include // For execv and fork. #include +#include +#include "base/eintr_wrapper.h" #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" @@ -204,27 +209,81 @@ bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { return false; } +int UserCollector::ForkExecAndPipe(std::vector &arguments, + const char *output_file) { + // Copy off a writeable version of arguments. + scoped_array argv(new char *[arguments.size() + 1]); + int total_args_size = 0; + for (size_t i = 0; i < arguments.size(); ++i) { + if (arguments[i] == NULL) { + logger_->LogError("Bad parameter"); + return -1; + } + total_args_size += strlen(arguments[i]) + 1; + } + scoped_array buffer(new char[total_args_size]); + char *buffer_pointer = &buffer[0]; + + for (size_t i = 0; i < arguments.size(); ++i) { + argv[i] = buffer_pointer; + strcpy(buffer_pointer, arguments[i]); + buffer_pointer += strlen(arguments[i]); + *buffer_pointer = '\0'; + ++buffer_pointer; + } + argv[arguments.size()] = NULL; + + int pid = fork(); + if (pid < 0) { + logger_->LogError("Fork failed: %d", errno); + return -1; + } + + if (pid == 0) { + int output_handle = creat(output_file, 0700); + if (output_handle < 0) { + logger_->LogError("Could not create %s: %d", output_file, errno); + // Avoid exit() to avoid atexit handlers from parent. + _exit(127); + } + dup2(output_handle, 1); + dup2(output_handle, 2); + execv(argv[0], &argv[0]); + logger_->LogError("Exec failed: %d", errno); + _exit(127); + } + + int status = 0; + if (HANDLE_EINTR(waitpid(pid, &status, 0)) < 0) { + logger_->LogError("Problem waiting for pid: %d", errno); + return -1; + } + if (!WIFEXITED(status)) { + logger_->LogError("Process did not exit normally: %x", status); + return -1; + } + return WEXITSTATUS(status); +} + bool UserCollector::ConvertCoreToMinidump(const FilePath &core_path, const FilePath &procfs_directory, const FilePath &minidump_path, const FilePath &temp_directory) { - // TODO(kmixter): Rewrite to use process_util once it's included in - // libchrome. FilePath output_path = temp_directory.Append("output"); - std::string core2md_command = - StringPrintf("\"%s\" \"%s\" \"%s\" \"%s\" > \"%s\" 2>&1", - kCoreToMinidumpConverterPath, - core_path.value().c_str(), - procfs_directory.value().c_str(), - minidump_path.value().c_str(), - output_path.value().c_str()); - int errorlevel = system(core2md_command.c_str()); + std::vector core2md_arguments; + core2md_arguments.push_back(kCoreToMinidumpConverterPath); + core2md_arguments.push_back(core_path.value().c_str()); + core2md_arguments.push_back(procfs_directory.value().c_str()); + core2md_arguments.push_back(minidump_path.value().c_str()); + + int errorlevel = ForkExecAndPipe(core2md_arguments, + output_path.value().c_str()); std::string output; file_util::ReadFileToString(output_path, &output); if (errorlevel != 0) { logger_->LogInfo("Problem during %s [result=%d]: %s", - core2md_command.c_str(), + kCoreToMinidumpConverterPath, errorlevel, output.c_str()); return false; diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 484445fcb..7d6c7110f 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -6,6 +6,7 @@ #define _CRASH_REPORTER_USER_COLLECTOR_H_ #include +#include #include "crash-reporter/crash_collector.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST @@ -51,6 +52,7 @@ class UserCollector : public CrashCollector { FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPath); FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPid); FRIEND_TEST(UserCollectorTest, CopyOffProcFilesOK); + FRIEND_TEST(UserCollectorTest, ForkExecAndPipe); FRIEND_TEST(UserCollectorTest, GetIdFromStatus); FRIEND_TEST(UserCollectorTest, GetProcessPath); FRIEND_TEST(UserCollectorTest, GetSymlinkTarget); @@ -66,6 +68,8 @@ class UserCollector : public CrashCollector { kIdMax }; + static const int kForkProblem = 255; + std::string GetPattern(bool enabled) const; bool SetUpInternal(bool enabled); @@ -86,6 +90,8 @@ class UserCollector : public CrashCollector { bool GetCreatedCrashDirectory(pid_t pid, FilePath *crash_file_path); bool CopyStdinToCoreFile(const FilePath &core_path); + int ForkExecAndPipe(std::vector &arguments, + const char *output_file); bool ConvertCoreToMinidump(const FilePath &core_path, const FilePath &procfs_directory, const FilePath &minidump_path, diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 3390caab9..9dc0d6098 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -15,6 +15,12 @@ static bool s_metrics = false; static const char kFilePath[] = "/my/path"; +// This test assumes the following standard binaries are installed. +static const char kBinBash[] = "/bin/bash"; +static const char kBinCp[] = "/bin/cp"; +static const char kBinEcho[] = "/bin/echo"; +static const char kBinFalse[] = "/bin/false"; + void CountCrash() { ++s_crashes; } @@ -38,17 +44,22 @@ class UserCollectorTest : public ::testing::Test { protected: void TestEnableOK(bool generate_diagnostics); + void ExpectFileEquals(const char *golden, + const char *file_path) { + std::string contents; + EXPECT_TRUE(file_util::ReadFileToString(FilePath(file_path), + &contents)); + EXPECT_EQ(golden, contents); + } + SystemLoggingMock logging_; UserCollector collector_; pid_t pid_; }; TEST_F(UserCollectorTest, EnableOK) { - std::string contents; ASSERT_TRUE(collector_.Enable()); - ASSERT_TRUE(file_util::ReadFileToString(FilePath("test/core_pattern"), - &contents)); - ASSERT_EQ("|/my/path --signal=%s --pid=%p", contents); + ExpectFileEquals("|/my/path --signal=%s --pid=%p", "test/core_pattern"); ASSERT_EQ(s_crashes, 0); ASSERT_NE(logging_.log().find("Enabling user crash handling"), std::string::npos); @@ -65,11 +76,8 @@ TEST_F(UserCollectorTest, EnableNoFileAccess) { } TEST_F(UserCollectorTest, DisableOK) { - std::string contents; ASSERT_TRUE(collector_.Disable()); - ASSERT_TRUE(file_util::ReadFileToString(FilePath("test/core_pattern"), - &contents)); - ASSERT_EQ("core", contents); + ExpectFileEquals("core", "test/core_pattern"); ASSERT_EQ(s_crashes, 0); ASSERT_NE(logging_.log().find("Disabling user crash handling"), std::string::npos); @@ -85,6 +93,69 @@ TEST_F(UserCollectorTest, DisableNoFileAccess) { std::string::npos); } +TEST_F(UserCollectorTest, ForkExecAndPipe) { + std::vector args; + char output_file[] = "test/fork_out"; + + // Test basic call with stdout. + args.clear(); + args.push_back(kBinEcho); + args.push_back("hello world"); + EXPECT_EQ(0, collector_.ForkExecAndPipe(args, output_file)); + ExpectFileEquals("hello world\n", output_file); + EXPECT_EQ("", logging_.log()); + + // Test non-zero return value + logging_.clear(); + args.clear(); + args.push_back(kBinFalse); + EXPECT_EQ(1, collector_.ForkExecAndPipe(args, output_file)); + ExpectFileEquals("", output_file); + EXPECT_EQ("", logging_.log()); + + // Test bad output_file. + EXPECT_EQ(127, collector_.ForkExecAndPipe(args, "/bad/path")); + + // Test bad executable. + logging_.clear(); + args.clear(); + args.push_back("false"); + EXPECT_EQ(127, collector_.ForkExecAndPipe(args, output_file)); + + // Test stderr captured. + std::string contents; + logging_.clear(); + args.clear(); + args.push_back(kBinCp); + EXPECT_EQ(1, collector_.ForkExecAndPipe(args, output_file)); + EXPECT_TRUE(file_util::ReadFileToString(FilePath(output_file), + &contents)); + EXPECT_NE(std::string::npos, contents.find("missing file operand")); + EXPECT_EQ("", logging_.log()); + + // NULL parameter. + logging_.clear(); + args.clear(); + args.push_back(NULL); + EXPECT_EQ(-1, collector_.ForkExecAndPipe(args, output_file)); + EXPECT_NE(std::string::npos, + logging_.log().find("Bad parameter")); + + // No parameters. + args.clear(); + EXPECT_EQ(127, collector_.ForkExecAndPipe(args, output_file)); + + // Segmentation faulting process. + logging_.clear(); + args.clear(); + args.push_back(kBinBash); + args.push_back("-c"); + args.push_back("kill -SEGV $$"); + EXPECT_EQ(-1, collector_.ForkExecAndPipe(args, output_file)); + EXPECT_NE(std::string::npos, + logging_.log().find("Process did not exit normally")); +} + TEST_F(UserCollectorTest, HandleCrashWithoutMetrics) { s_metrics = false; collector_.HandleCrash(10, 20, "foobar"); From 133983b2bb5b7316bef972a2d6ab93e3518d0811 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Wed, 20 Oct 2010 11:45:46 -0700 Subject: [PATCH 026/225] crash-reporter: Fix race between test code and rsyslogd Change-Id: I2297d03f7da88b0fbde1303411228b4d73ec6bf1 BUG=7765 TEST=Run crashsender 1000 times Review URL: http://codereview.chromium.org/3893005 --- crash_reporter/crash_sender | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 684986468..9d34e34b0 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -68,6 +68,12 @@ lecho() { logger -t "${TAG}" "$@" } +# Returns true if mock is enabled. +is_mock() { + [ -f "${MOCK_CRASH_SENDING}" ] && return 0 + return 1 +} + cleanup() { if [ -n "${TMP_DIR}" ]; then rm -rf "${TMP_DIR}" @@ -75,6 +81,11 @@ cleanup() { if [ ${CLEAN_UP_RUN_FILE} -eq 1 ]; then rm -f "${RUN_FILE}" fi + if is_mock; then + # For testing purposes, emit a message to log so that we + # know when the test has received all the messages from this run. + lecho "crash_sender done." + fi } check_not_already_running() { @@ -165,12 +176,6 @@ get_key_value() { grep "$2=" "$1" | cut -d = -f 2- } -# Returns true if mock is enabled. -is_mock() { - [ -f "${MOCK_CRASH_SENDING}" ] && return 0 - return 1 -} - # Return the board name. get_board() { echo $(get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_BOARD") From 0096b43d0fab581fb5e33dfe25f29ce9509a0152 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Wed, 20 Oct 2010 14:38:53 -0700 Subject: [PATCH 027/225] crash-reporter: avoid deadlock if dbus-daemon is hanging Change-Id: I40ba7fb556217bf0bea156e0496145d7195a1229 BUG=7783 TEST=sudo pkill -STOP dbus-daemon;sleep 100&ill -SEGV $!;pgrep crash_reporter shows crash_reporter finished dumping sleep, even though dbus-daemon is unreachable. Also ran UserCrash and CrashSender. Review URL: http://codereview.chromium.org/3976001 --- crash_reporter/crash_reporter.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index d85838698..2f8e987db 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -67,7 +67,7 @@ static void CountUserCrash() { kCrashKindUser, kCrashKindMax); std::string command = StringPrintf( - "/usr/bin/dbus-send --type=signal --system / \"%s\"", + "/usr/bin/dbus-send --type=signal --system / \"%s\" &", kUserCrashSignal); // Announce through D-Bus whenever a user crash happens. This is // used by the metrics daemon to log active use time between @@ -77,8 +77,18 @@ static void CountUserCrash() { // using a dbus library directly. However, this should run // relatively rarely and longer term we may need to implement a // better way to do this that doesn't rely on D-Bus. + // + // We run in the background in case dbus daemon itself is crashed + // and not responding. This allows us to not block and potentially + // deadlock on a dbus-daemon crash. If dbus-daemon crashes without + // restarting, each crash will fork off a lot of dbus-send + // processes. Such a system is in a unusable state and will need + // to be restarted anyway. - int status __attribute__((unused)) = system(command.c_str()); + int status = system(command.c_str()); + if (status != 0) { + s_system_log.LogWarning("dbus-send running failed"); + } } static int Initialize(KernelCollector *kernel_collector, From afcf80821c57a189b53b7a66f76d13855d63821e Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Tue, 26 Oct 2010 14:45:01 -0700 Subject: [PATCH 028/225] crash-reporter: Generate kernel crash signatures for server-side grouping of similar crashes BUG=5868 TEST=KernelCrash, UserCrash, CrashSender, unittests, and manual inspection of sent report. Change-Id: I31991895c9ac719ac1832d588ae3360500ef0c26 Review URL: http://codereview.chromium.org/4018008 --- crash_reporter/Makefile | 4 +- crash_reporter/crash_collector.cc | 16 +- crash_reporter/crash_collector.h | 8 + crash_reporter/crash_collector_test.cc | 24 +++ crash_reporter/crash_reporter.cc | 25 +++ crash_reporter/crash_sender | 17 +- crash_reporter/kernel_collector.cc | 203 ++++++++++++++++++++++-- crash_reporter/kernel_collector.h | 19 +++ crash_reporter/kernel_collector_test.cc | 114 ++++++++++++- crash_reporter/user_collector.cc | 2 + crash_reporter/user_collector_test.cc | 2 - 11 files changed, 411 insertions(+), 23 deletions(-) diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index fc2d68013..0523c199e 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -17,8 +17,10 @@ TEST_BINS = \ unclean_shutdown_collector_test \ user_collector_test +LDCONFIG = $(shell $(PKG_CONFIG) --libs libpcrecpp) + # -lglib-2.0 is needed by libbase.a now. -COMMON_LIBS = -lbase -lpthread -lglib-2.0 -lgflags -lrt +COMMON_LIBS = -lbase -lpthread -lglib-2.0 -lgflags -lrt $(LDCONFIG) REPORTER_LIBS = $(COMMON_LIBS) -lmetrics TEST_LIBS = $(COMMON_LIBS) -lgtest -lgmock diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 0391fa541..5ea06e288 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -39,7 +39,9 @@ static const uid_t kRootGroup = 0; // number of core files or minidumps reaches this number. const int CrashCollector::kMaxCrashDirectorySize = 32; -CrashCollector::CrashCollector() : forced_crash_directory_(NULL) { +CrashCollector::CrashCollector() + : forced_crash_directory_(NULL), + lsb_release_(kLsbRelease) { } CrashCollector::~CrashCollector() { @@ -247,12 +249,17 @@ bool CrashCollector::ReadKeyValueFile( return !any_errors; } +void CrashCollector::AddCrashMetaData(const std::string &key, + const std::string &value) { + extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str())); +} + void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, const std::string &exec_name, const std::string &payload_path) { std::map contents; - if (!ReadKeyValueFile(FilePath(std::string(kLsbRelease)), '=', &contents)) { - logger_->LogError("Problem parsing %s", kLsbRelease); + if (!ReadKeyValueFile(FilePath(std::string(lsb_release_)), '=', &contents)) { + logger_->LogError("Problem parsing %s", lsb_release_); // Even though there was some failure, take as much as we could read. } std::string version("unknown"); @@ -262,10 +269,11 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, } int64 payload_size = -1; file_util::GetFileSize(FilePath(payload_path), &payload_size); - std::string meta_data = StringPrintf("exec_name=%s\n" + std::string meta_data = StringPrintf("%sexec_name=%s\n" "ver=%s\n" "payload_size=%lld\n" "done=1\n", + extra_metadata_.c_str(), exec_name.c_str(), version.c_str(), payload_size); diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 97bf0d6e5..b4cabf957 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -40,6 +40,7 @@ class CrashCollector { FRIEND_TEST(CrashCollectorTest, GetCrashDirectoryInfo); FRIEND_TEST(CrashCollectorTest, FormatDumpBasename); FRIEND_TEST(CrashCollectorTest, Initialize); + FRIEND_TEST(CrashCollectorTest, MetaData); FRIEND_TEST(CrashCollectorTest, ReadKeyValueFile); FRIEND_TEST(CrashCollectorTest, Sanitize); @@ -87,6 +88,11 @@ class CrashCollector { char separator, std::map *dictionary); + // Add non-standard meta data to the crash metadata file. Call + // before calling WriteCrashMetaData. Key must not contain "=" or + // "\n" characters. Value must not contain "\n" characters. + void AddCrashMetaData(const std::string &key, const std::string &value); + // Write a file of metadata about crash. void WriteCrashMetaData(const FilePath &meta_path, const std::string &exec_name, @@ -95,7 +101,9 @@ class CrashCollector { CountCrashFunction count_crash_function_; IsFeedbackAllowedFunction is_feedback_allowed_function_; SystemLogging *logger_; + std::string extra_metadata_; const char *forced_crash_directory_; + const char *lsb_release_; }; #endif // _CRASH_REPORTER_CRASH_COLLECTOR_H_ diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 2dbf2bb2d..ffab06af8 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -218,6 +218,30 @@ TEST_F(CrashCollectorTest, ReadKeyValueFile) { EXPECT_TRUE(i != dictionary.end() && i->second == ""); } +TEST_F(CrashCollectorTest, MetaData) { + FilePath meta_file = test_dir_.Append("generated.meta"); + FilePath lsb_release = test_dir_.Append("lsb-release"); + FilePath payload_file = test_dir_.Append("payload-file"); + std::string contents; + collector_.lsb_release_ = lsb_release.value().c_str(); + const char kLsbContents[] = "CHROMEOS_RELEASE_VERSION=version\n"; + ASSERT_TRUE( + file_util::WriteFile(lsb_release, + kLsbContents, strlen(kLsbContents))); + const char kPayload[] = "foo"; + ASSERT_TRUE( + file_util::WriteFile(payload_file, + kPayload, strlen(kPayload))); + collector_.AddCrashMetaData("foo", "bar"); + collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value()); + EXPECT_TRUE(file_util::ReadFileToString(meta_file, &contents)); + EXPECT_EQ("foo=bar\n" + "exec_name=kernel\n" + "ver=version\n" + "payload_size=3\n" + "done=1\n", contents); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 2f8e987db..07dcd9c5c 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -18,6 +18,8 @@ #pragma GCC diagnostic ignored "-Wstrict-aliasing" DEFINE_bool(init, false, "Initialize crash logging"); DEFINE_bool(clean_shutdown, false, "Signal clean shutdown"); +DEFINE_string(generate_kernel_signature, "", + "Generate signature from given kcrash file"); DEFINE_bool(crash_test, false, "Crash test"); DEFINE_int32(pid, -1, "Crashing PID"); DEFINE_int32(signal, -1, "Signal causing crash"); @@ -146,6 +148,25 @@ static int HandleUserCrash(UserCollector *user_collector) { return 0; } +// Interactive/diagnostics mode for generating kernel crash signatures. +static int GenerateKernelSignature(KernelCollector *kernel_collector) { + std::string kcrash_contents; + std::string signature; + if (!file_util::ReadFileToString(FilePath(FLAGS_generate_kernel_signature), + &kcrash_contents)) { + fprintf(stderr, "Could not read file.\n"); + return 1; + } + if (!kernel_collector->ComputeKernelStackSignature( + kcrash_contents, + &signature, + true)) { + fprintf(stderr, "Signature could not be generated.\n"); + return 1; + } + printf("Kernel crash signature is \"%s\".\n", signature.c_str()); + return 0; +} int main(int argc, char *argv[]) { google::ParseCommandLineFlags(&argc, &argv, true); @@ -185,5 +206,9 @@ int main(int argc, char *argv[]) { return 0; } + if (!FLAGS_generate_kernel_signature.empty()) { + return GenerateKernelSignature(&kernel_collector); + } + return HandleUserCrash(&user_collector); } diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 9d34e34b0..ebb380e80 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -201,6 +201,7 @@ send_crash() { local hwclass="$(get_hardware_class)" local payload_extension="${kind}" local write_payload_size="$(get_key_value "${meta_path}" "payload_size")" + local sig="$(get_key_value "${meta_path}" "sig")" [ "${kind}" = "minidump" ] && payload_extension="dmp" local report_payload="$(get_base "${meta_path}").${payload_extension}" local send_payload_size="$(stat --printf=%s "${report_payload}")" @@ -214,6 +215,7 @@ send_crash() { lecho " URL: ${url}" lecho " Board: ${board}" lecho " HWClass: ${hwclass}" + [ "${sig}" != "undefined" ] && lecho " Sig: ${sig}" fi lecho " Exec name: ${exec_name}" if is_mock; then @@ -235,6 +237,17 @@ send_crash() { local report_id="${TMP_DIR}/report_id" local curl_stderr="${TMP_DIR}/curl_stderr" + local extra_key1="write_payload_size" + local extra_value1="${write_payload_size}" + local extra_key2="send_payload_size" + local extra_value2="${send_payload_size}" + if [ "${kind}" = "kcrash" ]; then + extra_key1="sig" + extra_value1="${sig}" + extra_key2="sig2" + extra_value2="${sig}" + fi + set +e curl "${url}" \ -F "prod=${CHROMEOS_PRODUCT}" \ @@ -243,8 +256,8 @@ send_crash() { -F "board=${board}" \ -F "hwclass=${hwclass}" \ -F "exec_name=${exec_name}" \ - -F "write_payload_size=${write_payload_size}" \ - -F "send_payload_size=${send_payload_size}" \ + -F "${extra_key1}=${extra_value1}" \ + -F "${extra_key2}=${extra_value2}" \ -F "guid=<${CONSENT_ID}" -o "${report_id}" 2>"${curl_stderr}" curl_result=$? set -e diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index c6bb33fca..a95d757db 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -9,12 +9,21 @@ #include "base/string_util.h" #include "crash-reporter/system_logging.h" -static const char kKernelExecName[] = "kernel"; -static const char kPreservedDumpPath[] = "/sys/kernel/debug/preserved/kcrash"; -const pid_t kKernelPid = 0; -const uid_t kRootUid = 0; - const char KernelCollector::kClearingSequence[] = " "; +static const char kDefaultKernelStackSignature[] = + "kernel-UnspecifiedStackSignature"; +static const char kKernelExecName[] = "kernel"; +const pid_t kKernelPid = 0; +static const char kKernelSignatureKey[] = "sig"; +// Byte length of maximum human readable portion of a kernel crash signature. +static const int kMaxHumanStringLength = 40; +static const char kPreservedDumpPath[] = "/sys/kernel/debug/preserved/kcrash"; +const uid_t kRootUid = 0; +// Time in seconds from the final kernel log message for a call stack +// to count towards the signature of the kcrash. +static const int kSignatureTimestampWindow = 2; +// Kernel log timestamp regular expression. +static const std::string kTimestampRegex("^<.*>\\[\\s*(\\d+\\.\\d+)\\]"); KernelCollector::KernelCollector() : is_enabled_(false), @@ -66,6 +75,171 @@ bool KernelCollector::ClearPreservedDump() { return true; } +// Hash a string to a number. We define our own hash function to not +// be dependent on a C++ library that might change. This function +// uses basically the same approach as tr1/functional_hash.h but with +// a larger prime number (16127 vs 131). +static unsigned HashString(const std::string &input) { + unsigned hash = 0; + for (size_t i = 0; i < input.length(); ++i) + hash = hash * 16127 + input[i]; + return hash; +} + +void KernelCollector::ProcessStackTrace( + pcrecpp::StringPiece kernel_dump, + bool print_diagnostics, + unsigned *hash, + float *last_stack_timestamp) { + pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE()); + pcrecpp::RE stack_trace_start_re(kTimestampRegex + " Call Trace:$"); + // Match lines such as the following and grab out "error_code". + // <4>[ 6066.849504] [<7937bcee>] error_code+0x66/0x6c + pcrecpp::RE stack_entry_re(kTimestampRegex + + " \\[<.*>\\]([\\s\\?]+)([^\\+ ]+)"); + std::string line; + std::string hashable; + + *hash = 0; + *last_stack_timestamp = 0; + + while (line_re.FindAndConsume(&kernel_dump, &line)) { + std::string certainty; + std::string function_name; + if (stack_trace_start_re.PartialMatch(line, last_stack_timestamp)) { + if (print_diagnostics) { + printf("Stack trace starting. Clearing any prior traces.\n"); + } + hashable.clear(); + } else if (stack_entry_re.PartialMatch(line, + last_stack_timestamp, + &certainty, + &function_name)) { + bool is_certain = certainty.find('?') == std::string::npos; + if (print_diagnostics) { + printf("@%f: stack entry for %s (%s)\n", + *last_stack_timestamp, + function_name.c_str(), + is_certain ? "certain" : "uncertain"); + } + // Do not include any uncertain (prefixed by '?') frames in our hash. + if (!is_certain) + continue; + if (!hashable.empty()) + hashable.append("|"); + hashable.append(function_name); + } + } + + *hash = HashString(hashable); + + if (print_diagnostics) { + printf("Hash based on stack trace: \"%s\" at %f.\n", + hashable.c_str(), *last_stack_timestamp); + } +} + +bool KernelCollector::FindCrashingFunction( + pcrecpp::StringPiece kernel_dump, + bool print_diagnostics, + float stack_trace_timestamp, + std::string *crashing_function) { + pcrecpp::RE eip_re(kTimestampRegex + " EIP: \\[<.*>\\] ([^\\+ ]+).*", + pcrecpp::MULTILINE()); + float timestamp = 0; + while (eip_re.FindAndConsume(&kernel_dump, ×tamp, crashing_function)) { + if (print_diagnostics) { + printf("@%f: found crashing function %s\n", + timestamp, + crashing_function->c_str()); + } + } + if (timestamp == 0) { + if (print_diagnostics) { + printf("Found no crashing function.\n"); + } + return false; + } + if (stack_trace_timestamp != 0 && + abs(stack_trace_timestamp - timestamp) > kSignatureTimestampWindow) { + if (print_diagnostics) { + printf("Found crashing function but not within window.\n"); + } + return false; + } + if (print_diagnostics) { + printf("Found crashing function %s\n", crashing_function->c_str()); + } + return true; +} + +bool KernelCollector::FindPanicMessage(pcrecpp::StringPiece kernel_dump, + bool print_diagnostics, + std::string *panic_message) { + // Match lines such as the following and grab out "Fatal exception" + // <0>[ 342.841135] Kernel panic - not syncing: Fatal exception + pcrecpp::RE kernel_panic_re(kTimestampRegex + + " Kernel panic[^\\:]*\\:\\s*(.*)", + pcrecpp::MULTILINE()); + float timestamp = 0; + while (kernel_panic_re.FindAndConsume(&kernel_dump, + ×tamp, + panic_message)) { + if (print_diagnostics) { + printf("@%f: panic message %s\n", + timestamp, + panic_message->c_str()); + } + } + if (timestamp == 0) { + if (print_diagnostics) { + printf("Found no panic message.\n"); + } + return false; + } + return true; +} + +bool KernelCollector::ComputeKernelStackSignature( + const std::string &kernel_dump, + std::string *kernel_signature, + bool print_diagnostics) { + unsigned stack_hash = 0; + float last_stack_timestamp = 0; + std::string human_string; + + ProcessStackTrace(kernel_dump, + print_diagnostics, + &stack_hash, + &last_stack_timestamp); + + if (!FindCrashingFunction(kernel_dump, + print_diagnostics, + last_stack_timestamp, + &human_string)) { + if (!FindPanicMessage(kernel_dump, print_diagnostics, &human_string)) { + if (print_diagnostics) { + printf("Found no human readable string, using empty string.\n"); + } + human_string.clear(); + } + } + + if (human_string.empty() && stack_hash == 0) { + if (print_diagnostics) { + printf("Found neither a stack nor a human readable string, failing.\n"); + } + return false; + } + + human_string = human_string.substr(0, kMaxHumanStringLength); + *kernel_signature = StringPrintf("%s-%s-%08X", + kKernelExecName, + human_string.c_str(), + stack_hash); + return true; +} + bool KernelCollector::Collect() { std::string kernel_dump; FilePath root_crash_directory; @@ -75,9 +249,19 @@ bool KernelCollector::Collect() { if (kernel_dump.empty()) { return false; } - logger_->LogInfo("Received prior crash notification from kernel"); + std::string signature; + if (!ComputeKernelStackSignature(kernel_dump, &signature, false)) { + signature = kDefaultKernelStackSignature; + } - if (is_feedback_allowed_function_()) { + bool feedback = is_feedback_allowed_function_(); + + logger_->LogInfo("Received prior crash notification from " + "kernel (signature %s) (%s)", + signature.c_str(), + feedback ? "handling" : "ignoring"); + + if (feedback) { count_crash_function_(); if (!GetCreatedCrashDirectoryByEuid(kRootUid, @@ -101,16 +285,15 @@ bool KernelCollector::Collect() { return true; } + AddCrashMetaData(kKernelSignatureKey, signature); WriteCrashMetaData( root_crash_directory.Append( StringPrintf("%s.meta", dump_basename.c_str())), kKernelExecName, kernel_crash_path.value()); - logger_->LogInfo("Collected kernel crash diagnostics into %s", + logger_->LogInfo("Stored kcrash to %s", kernel_crash_path.value().c_str()); - } else { - logger_->LogInfo("Crash not saved since metrics disabled"); } if (!ClearPreservedDump()) { return false; diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index e6cd57392..708fd1b9e 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -5,6 +5,8 @@ #ifndef _CRASH_REPORTER_KERNEL_COLLECTOR_H_ #define _CRASH_REPORTER_KERNEL_COLLECTOR_H_ +#include + #include #include "base/file_path.h" @@ -34,6 +36,11 @@ class KernelCollector : public CrashCollector { // a dump (even if there were problems storing the dump), false otherwise. bool Collect(); + // Compute a stack signature string from a kernel dump. + bool ComputeKernelStackSignature(const std::string &kernel_dump, + std::string *kernel_signature, + bool print_diagnostics); + private: friend class KernelCollectorTest; FRIEND_TEST(KernelCollectorTest, ClearPreservedDump); @@ -43,6 +50,18 @@ class KernelCollector : public CrashCollector { bool LoadPreservedDump(std::string *contents); bool ClearPreservedDump(); + void ProcessStackTrace(pcrecpp::StringPiece kernel_dump, + bool print_diagnostics, + unsigned *hash, + float *last_stack_timestamp); + bool FindCrashingFunction(pcrecpp::StringPiece kernel_dump, + bool print_diagnostics, + float stack_trace_timestamp, + std::string *crashing_function); + bool FindPanicMessage(pcrecpp::StringPiece kernel_dump, + bool print_diagnostics, + std::string *panic_message); + bool is_enabled_; FilePath preserved_dump_path_; static const char kClearingSequence[]; diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 44a5ba0a7..93b253da6 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -135,19 +135,18 @@ TEST_F(KernelCollectorTest, CollectOptedOut) { SetUpSuccessfulCollect(); s_metrics = false; ASSERT_TRUE(collector_.Collect()); - ASSERT_NE(std::string::npos, - logging_.log().find("Crash not saved since metrics disabled")); + ASSERT_NE(std::string::npos, logging_.log().find("(ignoring)")); ASSERT_EQ(0, s_crashes); CheckPreservedDumpClear(); } - TEST_F(KernelCollectorTest, CollectOK) { SetUpSuccessfulCollect(); ASSERT_TRUE(collector_.Collect()); ASSERT_EQ(1, s_crashes); - static const char kNamePrefix[] = "Collected kernel crash diagnostics into "; + ASSERT_NE(std::string::npos, logging_.log().find("(handling)")); + static const char kNamePrefix[] = "Stored kcrash to "; size_t pos = logging_.log().find(kNamePrefix); ASSERT_NE(std::string::npos, pos); pos += strlen(kNamePrefix); @@ -165,6 +164,113 @@ TEST_F(KernelCollectorTest, CollectOK) { CheckPreservedDumpClear(); } +TEST_F(KernelCollectorTest, ComputeKernelStackSignature) { + const char kBugToPanic[] = + "<4>[ 6066.829029] [<79039d16>] ? run_timer_softirq+0x165/0x1e6\n" + "<4>[ 6066.829029] [<790340af>] ignore_old_stack+0xa6/0x143\n" + "<0>[ 6066.829029] EIP: [] ieee80211_stop_tx_ba_session+" + "0xa3/0xb5 [mac80211] SS:ESP 0068:7951febc\n" + "<0>[ 6066.829029] CR2: 00000000323038a7\n" + "<4>[ 6066.845422] ---[ end trace 12b058bb46c43500 ]---\n" + "<0>[ 6066.845747] Kernel panic - not syncing: Fatal exception " + "in interrupt\n" + "<0>[ 6066.846902] Call Trace:\n" + "<4>[ 6066.846902] [<7937a07b>] ? printk+0x14/0x19\n" + "<4>[ 6066.949779] [<79379fc1>] panic+0x3e/0xe4\n" + "<4>[ 6066.949971] [<7937c5c5>] oops_end+0x73/0x81\n" + "<4>[ 6066.950208] [<7901b260>] no_context+0x10d/0x117\n"; + std::string signature; + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false)); + EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-DE253569", signature); + + const char kPCButNoStack[] = + "<0>[ 6066.829029] EIP: [] ieee80211_stop_tx_ba_session+"; + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kPCButNoStack, &signature, false)); + EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-00000000", signature); + + const char kStackButNoPC[] = + "<4>[ 6066.829029] [<790340af>] __do_softirq+0xa6/0x143\n"; + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kStackButNoPC, &signature, false)); + EXPECT_EQ("kernel--83615F0A", signature); + + const char kMissingEverything[] = + "<4>[ 6066.829029] [<790340af>] ? __do_softirq+0xa6/0x143\n"; + EXPECT_FALSE( + collector_.ComputeKernelStackSignature(kMissingEverything, + &signature, + false)); + + const char kBreakmeBug[] = + "<4>[ 180.492137] [<790970c6>] ? handle_mm_fault+0x67f/0x96d\n" + "<4>[ 180.492137] [<790dcdfe>] ? proc_reg_write+0x5f/0x73\n" + "<4>[ 180.492137] [<790e2224>] ? write_breakme+0x0/0x108\n" + "<4>[ 180.492137] [<790dcd9f>] ? proc_reg_write+0x0/0x73\n" + "<4>[ 180.492137] [<790ac0aa>] vfs_write+0x85/0xe4\n" + "<0>[ 180.492137] Code: c6 44 05 b2 00 89 d8 e8 0c ef 09 00 85 c0 75 " + "0b c7 00 00 00 00 00 e9 8e 00 00 00 ba e6 75 4b 79 89 d8 e8 f1 ee 09 " + "00 85 c0 75 04 <0f> 0b eb fe ba 58 47 49 79 89 d8 e8 dd ee 09 00 85 " + "c0 75 0a 68\n" + "<0>[ 180.492137] EIP: [<790e22a4>] write_breakme+0x80/0x108 SS:ESP " + "0068:aa3e9efc\n" + "<4>[ 180.501800] ---[ end trace 2a6b72965e1b1523 ]---\n" + "<0>[ 180.502026] Kernel panic - not syncing: Fatal exception\n" + "<4>[ 180.502026] Call Trace:\n" + "<4>[ 180.502806] [<79379aba>] ? printk+0x14/0x1a\n" + "<4>[ 180.503033] [<79379a00>] panic+0x3e/0xe4\n" + "<4>[ 180.503287] [<7937c005>] oops_end+0x73/0x81\n" + "<4>[ 180.503520] [<790055dd>] die+0x58/0x5e\n" + "<4>[ 180.503538] [<7937b96c>] do_trap+0x8e/0xa7\n" + "<4>[ 180.503555] [<79003d70>] ? do_invalid_op+0x0/0x80\n"; + + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kBreakmeBug, &signature, false)); + EXPECT_EQ("kernel-write_breakme-122AB3CD", signature); + + const char kPCLineTooOld[] = + "<4>[ 174.492137] [<790970c6>] ignored_function+0x67f/0x96d\n" + "<4>[ 175.492137] [<790970c6>] ignored_function2+0x67f/0x96d\n" + "<0>[ 174.492137] EIP: [<790e22a4>] write_breakme+0x80/0x108 SS:ESP " + "0068:aa3e9efc\n" + "<4>[ 180.501800] ---[ end trace 2a6b72965e1b1523 ]---\n" + "<4>[ 180.502026] Call Trace:\n" + "<0>[ 180.502026] Kernel panic - not syncing: Fatal exception\n" + "<4>[ 180.502806] [<79379aba>] printk+0x14/0x1a\n"; + + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kPCLineTooOld, &signature, false)); + EXPECT_EQ("kernel-Fatal exception-ED4C84FE", signature); + + // Panic without EIP line. + const char kExamplePanicOnly[] = + "<0>[ 87.485611] Kernel panic - not syncing: Testing panic\n" + "<4>[ 87.485630] Pid: 2825, comm: bash Tainted: G " + "C 2.6.32.23+drm33.10 #1\n" + "<4>[ 87.485639] Call Trace:\n" + "<4>[ 87.485660] [<8133f71d>] ? printk+0x14/0x17\n" + "<4>[ 87.485674] [<8133f663>] panic+0x3e/0xe4\n" + "<4>[ 87.485689] [<810d062e>] write_breakme+0xaa/0x124\n"; + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kExamplePanicOnly, + &signature, + false)); + EXPECT_EQ("kernel-Testing panic-E0FC3552", signature); + + // Long message. + const char kTruncatedMessage[] = + "<0>[ 87.485611] Kernel panic - not syncing: 01234567890123456789" + "01234567890123456789X\n"; + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kTruncatedMessage, + &signature, + false)); + EXPECT_EQ("kernel-0123456789012345678901234567890123456789-00000000", + signature); + +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index bc414ab7a..8d99b4729 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -332,6 +332,8 @@ bool UserCollector::GenerateDiagnostics(pid_t pid, container_dir)) { // temporary directory // Note we leave the container directory for inspection. conversion_result = false; + } else { + file_util::Delete(container_dir, true); } WriteCrashMetaData( diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 9dc0d6098..db707f5ac 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -42,8 +42,6 @@ class UserCollectorTest : public ::testing::Test { pid_ = getpid(); } protected: - void TestEnableOK(bool generate_diagnostics); - void ExpectFileEquals(const char *golden, const char *file_path) { std::string contents; From 207694d3f491ef602a859c30cc1379584f2d61cf Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Thu, 28 Oct 2010 15:42:37 -0700 Subject: [PATCH 029/225] crash-reporter: write conversion failure diagnostics into fake dmp files Change-Id: I9f1ca92def3e1d0fa43b3bef0f2a72d367953926 BUG=6299,7782 TEST=bvts Review URL: http://codereview.chromium.org/4088003 --- crash_reporter/crash_collector.cc | 16 +- crash_reporter/crash_collector.h | 18 ++- crash_reporter/crash_collector_test.cc | 13 ++ crash_reporter/crash_sender | 36 +++-- crash_reporter/kernel_collector.cc | 5 +- crash_reporter/kernel_collector_test.cc | 2 +- crash_reporter/user_collector.cc | 186 ++++++++++++++++-------- crash_reporter/user_collector.h | 25 +++- 8 files changed, 210 insertions(+), 91 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 5ea06e288..1e2cd1abd 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -86,6 +86,14 @@ std::string CrashCollector::FormatDumpBasename(const std::string &exec_name, pid); } +FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory, + const std::string &basename, + const std::string &extension) { + return crash_directory.Append(StringPrintf("%s.%s", + basename.c_str(), + extension.c_str())); +} + FilePath CrashCollector::GetCrashDirectoryInfo( uid_t process_euid, uid_t default_user_id, @@ -125,10 +133,13 @@ bool CrashCollector::GetUserInfoFromName(const std::string &name, } bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid, - FilePath *crash_directory) { + FilePath *crash_directory, + bool *out_of_capacity) { uid_t default_user_id; gid_t default_user_group; + if (out_of_capacity != NULL) *out_of_capacity = false; + // For testing. if (forced_crash_directory_ != NULL) { *crash_directory = FilePath(forced_crash_directory_); @@ -173,6 +184,7 @@ bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid, } if (!CheckHasCapacity(*crash_directory)) { + if (out_of_capacity != NULL) *out_of_capacity = true; return false; } @@ -271,11 +283,13 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, file_util::GetFileSize(FilePath(payload_path), &payload_size); std::string meta_data = StringPrintf("%sexec_name=%s\n" "ver=%s\n" + "payload=%s\n" "payload_size=%lld\n" "done=1\n", extra_metadata_.c_str(), exec_name.c_str(), version.c_str(), + payload_path.c_str(), payload_size); if (!file_util::WriteFile(meta_path, meta_data.c_str(), meta_data.size())) { logger_->LogError("Unable to write %s", meta_path.value().c_str()); diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index b4cabf957..e5eddcdd5 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -10,9 +10,9 @@ #include #include +#include "base/file_path.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST -class FilePath; class SystemLogging; // User crash collector. @@ -38,6 +38,7 @@ class CrashCollector { FRIEND_TEST(CrashCollectorTest, CheckHasCapacityStrangeNames); FRIEND_TEST(CrashCollectorTest, CheckHasCapacityUsual); FRIEND_TEST(CrashCollectorTest, GetCrashDirectoryInfo); + FRIEND_TEST(CrashCollectorTest, GetCrashPath); FRIEND_TEST(CrashCollectorTest, FormatDumpBasename); FRIEND_TEST(CrashCollectorTest, Initialize); FRIEND_TEST(CrashCollectorTest, MetaData); @@ -67,17 +68,26 @@ class CrashCollector { uid_t *uid, gid_t *gid); // Determines the crash directory for given eud, and creates the - // directory if necessary with appropriate permissions. Returns + // directory if necessary with appropriate permissions. If + // |out_of_capacity| is not NULL, it is set to indicate if the call + // failed due to not having capacity in the crash directory. Returns // true whether or not directory needed to be created, false on any - // failure. If the crash directory is already full, returns false. + // failure. If the crash directory is at capacity, returns false. bool GetCreatedCrashDirectoryByEuid(uid_t euid, - FilePath *crash_file_path); + FilePath *crash_file_path, + bool *out_of_capacity); // Format crash name based on components. std::string FormatDumpBasename(const std::string &exec_name, time_t timestamp, pid_t pid); + // Create a file path to a file in |crash_directory| with the given + // |basename| and |extension|. + FilePath GetCrashPath(const FilePath &crash_directory, + const std::string &basename, + const std::string &extension); + // Check given crash directory still has remaining capacity for another // crash. bool CheckHasCapacity(const FilePath &crash_directory); diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index ffab06af8..859653b13 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -120,6 +120,18 @@ TEST_F(CrashCollectorTest, FormatDumpBasename) { ASSERT_EQ("foo.20100523.135015.100", basename); } +TEST_F(CrashCollectorTest, GetCrashPath) { + EXPECT_EQ("/var/spool/crash/myprog.20100101.1200.1234.core", + collector_.GetCrashPath(FilePath("/var/spool/crash"), + "myprog.20100101.1200.1234", + "core").value()); + EXPECT_EQ("/home/chronos/user/crash/chrome.20100101.1200.1234.dmp", + collector_.GetCrashPath(FilePath("/home/chronos/user/crash"), + "chrome.20100101.1200.1234", + "dmp").value()); +} + + bool CrashCollectorTest::CheckHasCapacity() { static const char kFullMessage[] = "Crash directory test already full"; bool has_capacity = collector_.CheckHasCapacity(test_dir_); @@ -238,6 +250,7 @@ TEST_F(CrashCollectorTest, MetaData) { EXPECT_EQ("foo=bar\n" "exec_name=kernel\n" "ver=version\n" + "payload=test/payload-file\n" "payload_size=3\n" "done=1\n", contents); } diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index ebb380e80..c01a52d5c 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -152,20 +152,24 @@ get_base() { echo "${1%.*}" } +get_extension() { + echo "${1##*.}" +} + # Return which kind of report the given metadata file relates to get_kind() { - # There should never be a report with both a dmp and kcrash file. - # If that were to happen we arbitrarily consider this a minidump - # report and effectively ignore the kcrash. - local base="$(get_base "$1")" - if [ -r "${base}.dmp" ]; then + local payload="$(get_key_value "$1" "payload")" + if [ ! -r "${payload}" ]; then + lecho "Missing payload: ${payload}" + echo "unknown" + return + fi + local kind="$(get_extension "${payload}")" + if [ "${kind}" = "dmp" ]; then echo "minidump" return fi - if [ -r "${base}.kcrash" ]; then - echo "kcrash" - return - fi + echo "${kind}" } get_key_value() { @@ -192,6 +196,7 @@ get_hardware_class() { send_crash() { local meta_path="$1" + local report_payload="$(get_key_value "${meta_path}" "payload")" local kind="$(get_kind "${meta_path}")" local exec_name="$(get_key_value "${meta_path}" "exec_name")" local sleep_time=$(generate_uniform_random $SECONDS_SEND_SPREAD) @@ -199,12 +204,9 @@ send_crash() { local chromeos_version="$(get_key_value "${meta_path}" "ver")" local board="$(get_board)" local hwclass="$(get_hardware_class)" - local payload_extension="${kind}" local write_payload_size="$(get_key_value "${meta_path}" "payload_size")" local sig="$(get_key_value "${meta_path}" "sig")" - [ "${kind}" = "minidump" ] && payload_extension="dmp" - local report_payload="$(get_base "${meta_path}").${payload_extension}" - local send_payload_size="$(stat --printf=%s "${report_payload}")" + local send_payload_size="$(stat --printf=%s "${report_payload}" 2>/dev/null)" lecho "Sending crash:" lecho " Scheduled to send in ${sleep_time}s" lecho " Metadata: ${meta_path} (${kind})" @@ -241,7 +243,7 @@ send_crash() { local extra_value1="${write_payload_size}" local extra_key2="send_payload_size" local extra_value2="${send_payload_size}" - if [ "${kind}" = "kcrash" ]; then + if [ "${sig}" != "unknown" ]; then extra_key1="sig" extra_value1="${sig}" extra_key2="sig2" @@ -309,8 +311,10 @@ send_crashes() { lecho "Considering metadata ${meta_path}." local kind=$(get_kind "${meta_path}") - if [ "${kind}" != "minidump" ] && [ "${kind}" != "kcrash" ]; then - lecho "Unknown report kind. Removing report." + if [ "${kind}" != "minidump" ] && \ + [ "${kind}" != "kcrash" ] && \ + [ "${kind}" != "log" ]; then + lecho "Unknown report kind ${kind}. Removing report." remove_report "${meta_path}" continue fi diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index a95d757db..c95873347 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -259,13 +259,14 @@ bool KernelCollector::Collect() { logger_->LogInfo("Received prior crash notification from " "kernel (signature %s) (%s)", signature.c_str(), - feedback ? "handling" : "ignoring"); + feedback ? "handling" : "ignoring - no consent"); if (feedback) { count_crash_function_(); if (!GetCreatedCrashDirectoryByEuid(kRootUid, - &root_crash_directory)) { + &root_crash_directory, + NULL)) { return true; } diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 93b253da6..7b84229aa 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -135,7 +135,7 @@ TEST_F(KernelCollectorTest, CollectOptedOut) { SetUpSuccessfulCollect(); s_metrics = false; ASSERT_TRUE(collector_.Collect()); - ASSERT_NE(std::string::npos, logging_.log().find("(ignoring)")); + ASSERT_NE(std::string::npos, logging_.log().find("(ignoring - no consent)")); ASSERT_EQ(0, s_crashes); CheckPreservedDumpClear(); diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 8d99b4729..f9cb49400 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -19,7 +19,13 @@ #include "base/logging.h" #include "base/string_util.h" #include "crash-reporter/system_logging.h" +#include "gflags/gflags.h" +DEFINE_bool(core2md_failure_test, false, "Core2md failure test"); +DEFINE_bool(directory_failure_test, false, "Spool directory failure test"); + +static const char kCollectionErrorSignature[] = + "crash_reporter-user-collection"; // This procfs file is used to cause kernel core file writing to // instead pipe the core file into a user space process. See // core(5) man page. @@ -149,16 +155,41 @@ bool UserCollector::GetIdFromStatus(const char *prefix, return true; } +void UserCollector::LogCollectionError(const std::string &error_message) { + error_log_.append(error_message.c_str()); + error_log_.append("\n"); + logger_->LogError(error_message.c_str()); +} + +void UserCollector::EnqueueCollectionErrorLog(pid_t pid, + const std::string &exec) { + FilePath crash_path; + logger_->LogInfo("Writing conversion problems as separate crash report."); + if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) { + logger_->LogError("Could not even get log directory; out of space?"); + return; + } + std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); + FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); + FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); + file_util::WriteFile(log_path, + error_log_.data(), + error_log_.length()); + AddCrashMetaData("sig", kCollectionErrorSignature); + WriteCrashMetaData(meta_path, exec, log_path.value()); +} + bool UserCollector::CopyOffProcFiles(pid_t pid, const FilePath &container_dir) { if (!file_util::CreateDirectory(container_dir)) { - logger_->LogInfo("Could not create %s", container_dir.value().c_str()); + LogCollectionError(StringPrintf("Could not create %s", + container_dir.value().c_str())); return false; } FilePath process_path = GetProcessPath(pid); if (!file_util::PathExists(process_path)) { - logger_->LogWarning("Path %s does not exist", - process_path.value().c_str()); + LogCollectionError(StringPrintf("Path %s does not exist", + process_path.value().c_str())); return false; } static const char *proc_files[] = { @@ -171,7 +202,8 @@ bool UserCollector::CopyOffProcFiles(pid_t pid, for (unsigned i = 0; i < arraysize(proc_files); ++i) { if (!file_util::CopyFile(process_path.Append(proc_files[i]), container_dir.Append(proc_files[i]))) { - logger_->LogWarning("Could not copy %s file", proc_files[i]); + LogCollectionError(StringPrintf("Could not copy %s file", + proc_files[i])); return false; } } @@ -179,21 +211,31 @@ bool UserCollector::CopyOffProcFiles(pid_t pid, } bool UserCollector::GetCreatedCrashDirectory(pid_t pid, - FilePath *crash_file_path) { + FilePath *crash_file_path, + bool *out_of_capacity) { FilePath process_path = GetProcessPath(pid); std::string status; + if (FLAGS_directory_failure_test) { + LogCollectionError("Purposefully failing to create spool directory"); + return false; + } if (!file_util::ReadFileToString(process_path.Append("status"), &status)) { - logger_->LogError("Could not read status file"); + LogCollectionError("Could not read status file"); return false; } int process_euid; if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { - logger_->LogError("Could not find euid in status file"); + LogCollectionError("Could not find euid in status file"); return false; } - return GetCreatedCrashDirectoryByEuid(process_euid, - crash_file_path); + if (!GetCreatedCrashDirectoryByEuid(process_euid, + crash_file_path, + out_of_capacity)) { + LogCollectionError("Could not create crash directory"); + return false; + } + return true; } bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { @@ -203,7 +245,7 @@ bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { return true; } - logger_->LogError("Could not write core file"); + LogCollectionError("Could not write core file"); // If the file system was full, make sure we remove any remnants. file_util::Delete(core_path, false); return false; @@ -259,16 +301,16 @@ int UserCollector::ForkExecAndPipe(std::vector &arguments, return -1; } if (!WIFEXITED(status)) { - logger_->LogError("Process did not exit normally: %x", status); + logger_->LogError("Process did not exit normally: %d", status); return -1; } return WEXITSTATUS(status); } -bool UserCollector::ConvertCoreToMinidump(const FilePath &core_path, - const FilePath &procfs_directory, - const FilePath &minidump_path, - const FilePath &temp_directory) { +bool UserCollector::RunCoreToMinidump(const FilePath &core_path, + const FilePath &procfs_directory, + const FilePath &minidump_path, + const FilePath &temp_directory) { FilePath output_path = temp_directory.Append("output"); std::vector core2md_arguments; core2md_arguments.push_back(kCoreToMinidumpConverterPath); @@ -276,83 +318,99 @@ bool UserCollector::ConvertCoreToMinidump(const FilePath &core_path, core2md_arguments.push_back(procfs_directory.value().c_str()); core2md_arguments.push_back(minidump_path.value().c_str()); + if (FLAGS_core2md_failure_test) { + // To test how core2md errors are propagaged, cause an error + // by forgetting a required argument. + core2md_arguments.pop_back(); + } + int errorlevel = ForkExecAndPipe(core2md_arguments, output_path.value().c_str()); std::string output; file_util::ReadFileToString(output_path, &output); if (errorlevel != 0) { - logger_->LogInfo("Problem during %s [result=%d]: %s", - kCoreToMinidumpConverterPath, - errorlevel, - output.c_str()); + LogCollectionError(StringPrintf("Problem during %s [result=%d]: %s", + kCoreToMinidumpConverterPath, + errorlevel, + output.c_str())); return false; } if (!file_util::PathExists(minidump_path)) { - logger_->LogError("Minidump file %s was not created", - minidump_path.value().c_str()); + LogCollectionError(StringPrintf("Minidump file %s was not created", + minidump_path.value().c_str())); return false; } return true; } -bool UserCollector::GenerateDiagnostics(pid_t pid, - const std::string &exec_name) { - FilePath container_dir("/tmp"); - container_dir = container_dir.Append( - StringPrintf("crash_reporter.%d", pid)); - +bool UserCollector::ConvertCoreToMinidump(pid_t pid, + const FilePath &container_dir, + const FilePath &core_path, + const FilePath &minidump_path) { if (!CopyOffProcFiles(pid, container_dir)) { - file_util::Delete(container_dir, true); return false; } - FilePath crash_path; - if (!GetCreatedCrashDirectory(pid, &crash_path)) { - file_util::Delete(container_dir, true); - return false; - } - std::string dump_basename = FormatDumpBasename(exec_name, time(NULL), pid); - FilePath core_path = crash_path.Append( - StringPrintf("%s.core", dump_basename.c_str())); - if (!CopyStdinToCoreFile(core_path)) { - file_util::Delete(container_dir, true); return false; } - FilePath minidump_path = crash_path.Append( - StringPrintf("%s.dmp", dump_basename.c_str())); - - bool conversion_result = true; - if (!ConvertCoreToMinidump(core_path, - container_dir, // procfs directory - minidump_path, - container_dir)) { // temporary directory - // Note we leave the container directory for inspection. - conversion_result = false; - } else { - file_util::Delete(container_dir, true); - } - - WriteCrashMetaData( - crash_path.Append( - StringPrintf("%s.meta", dump_basename.c_str())), - exec_name, - minidump_path.value()); + bool conversion_result = RunCoreToMinidump( + core_path, + container_dir, // procfs directory + minidump_path, + container_dir); // temporary directory if (conversion_result) { logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str()); } + return conversion_result; +} + +bool UserCollector::ConvertAndEnqueueCrash(int pid, + const std::string &exec, + bool *out_of_capacity) { + FilePath crash_path; + if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) { + LogCollectionError("Unable to find/create process-specific crash path"); + return false; + } + + // Directory like /tmp/crash_reporter.1234 which contains the + // procfs entries and other temporary files used during conversion. + FilePath container_dir = FilePath("/tmp").Append( + StringPrintf("crash_reporter.%d", pid)); + std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); + FilePath core_path = GetCrashPath(crash_path, dump_basename, "core"); + FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); + FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp"); + + if (!ConvertCoreToMinidump(pid, container_dir, core_path, + minidump_path)) { + logger_->LogInfo("Leaving core file at %s due to conversion error", + core_path.value().c_str()); + return false; + } + + // Here we commit to sending this file. We must not return false + // after this point or we will generate a log report as well as a + // crash report. + WriteCrashMetaData(meta_path, + exec, + minidump_path.value()); + if (!file_util::PathExists(FilePath(kLeaveCoreFile))) { file_util::Delete(core_path, false); } else { - logger_->LogInfo("Leaving core file at %s", core_path.value().c_str()); + logger_->LogInfo("Leaving core file at %s due to developer image", + core_path.value().c_str()); } - return conversion_result; + file_util::Delete(container_dir, true); + return true; } bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { @@ -368,14 +426,20 @@ bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { bool feedback = is_feedback_allowed_function_(); logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", exec.c_str(), pid, signal, - feedback ? "handling" : "ignoring"); + feedback ? "handling" : "ignoring - no consent"); if (feedback) { count_crash_function_(); if (generate_diagnostics_) { - return GenerateDiagnostics(pid, exec); + bool out_of_capacity = false; + if (!ConvertAndEnqueueCrash(pid, exec, &out_of_capacity)) { + if (!out_of_capacity) + EnqueueCollectionErrorLog(pid, exec); + return false; + } } } + return true; } diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 7d6c7110f..2ed8e6f47 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -82,27 +82,40 @@ class UserCollector : public CrashCollector { IdKind kind, const std::string &status_contents, int *id); + + void LogCollectionError(const std::string &error_message); + void EnqueueCollectionErrorLog(pid_t pid, const std::string &exec_name); + bool CopyOffProcFiles(pid_t pid, const FilePath &process_map); // Determines the crash directory for given pid based on pid's owner, // and creates the directory if necessary with appropriate permissions. // Returns true whether or not directory needed to be created, false on // any failure. bool GetCreatedCrashDirectory(pid_t pid, - FilePath *crash_file_path); + FilePath *crash_file_path, + bool *out_of_capacity); bool CopyStdinToCoreFile(const FilePath &core_path); int ForkExecAndPipe(std::vector &arguments, const char *output_file); - bool ConvertCoreToMinidump(const FilePath &core_path, - const FilePath &procfs_directory, - const FilePath &minidump_path, - const FilePath &temp_directory); - bool GenerateDiagnostics(pid_t pid, const std::string &exec_name); + bool RunCoreToMinidump(const FilePath &core_path, + const FilePath &procfs_directory, + const FilePath &minidump_path, + const FilePath &temp_directory); + bool ConvertCoreToMinidump(pid_t pid, + const FilePath &container_dir, + const FilePath &core_path, + const FilePath &minidump_path); + bool ConvertAndEnqueueCrash(int pid, const std::string &exec_name, + bool *out_of_capacity); bool generate_diagnostics_; std::string core_pattern_file_; std::string our_path_; bool initialized_; + // String containing the current log of crash handling errors. + std::string error_log_; + static const char *kUserId; static const char *kGroupId; }; From 7f2830e138f5ed5e04b8a3a92765950a9f72e811 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Mon, 1 Nov 2010 13:37:41 -0700 Subject: [PATCH 030/225] crash-reporter: fix bug where we were sending "undefined" as all stack signatures Also, move the logic for picking which extra parameters to send up above where mock sending ends so UserCrash detects this regression in the future. And merge unknown and undefined special strings into one to avoid confusion. Change-Id: Ic55a0c5b473a0cef39eac2c1250d1607deffe050 BUG=8266 TEST=User/KernelCrash and CrashSender Review URL: http://codereview.chromium.org/4139011 --- crash_reporter/crash_sender | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index c01a52d5c..2e56dec58 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -161,7 +161,7 @@ get_kind() { local payload="$(get_key_value "$1" "payload")" if [ ! -r "${payload}" ]; then lecho "Missing payload: ${payload}" - echo "unknown" + echo "undefined" return fi local kind="$(get_extension "${payload}")" @@ -185,12 +185,12 @@ get_board() { echo $(get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_BOARD") } -# Return the hardware class or "unknown". +# Return the hardware class or "undefined". get_hardware_class() { if [ -r "${HWCLASS_PATH}" ]; then cat "${HWCLASS_PATH}" else - echo "unknown" + echo "undefined" fi } @@ -207,6 +207,18 @@ send_crash() { local write_payload_size="$(get_key_value "${meta_path}" "payload_size")" local sig="$(get_key_value "${meta_path}" "sig")" local send_payload_size="$(stat --printf=%s "${report_payload}" 2>/dev/null)" + + local extra_key1="write_payload_size" + local extra_value1="${write_payload_size}" + local extra_key2="send_payload_size" + local extra_value2="${send_payload_size}" + if [ "${sig}" != "undefined" ]; then + extra_key1="sig" + extra_value1="${sig}" + extra_key2="sig2" + extra_value2="${sig}" + fi + lecho "Sending crash:" lecho " Scheduled to send in ${sleep_time}s" lecho " Metadata: ${meta_path} (${kind})" @@ -217,7 +229,8 @@ send_crash() { lecho " URL: ${url}" lecho " Board: ${board}" lecho " HWClass: ${hwclass}" - [ "${sig}" != "undefined" ] && lecho " Sig: ${sig}" + lecho " ${extra_key1}: ${extra_value1}" + lecho " ${extra_key2}: ${extra_value2}" fi lecho " Exec name: ${exec_name}" if is_mock; then @@ -239,17 +252,6 @@ send_crash() { local report_id="${TMP_DIR}/report_id" local curl_stderr="${TMP_DIR}/curl_stderr" - local extra_key1="write_payload_size" - local extra_value1="${write_payload_size}" - local extra_key2="send_payload_size" - local extra_value2="${send_payload_size}" - if [ "${sig}" != "unknown" ]; then - extra_key1="sig" - extra_value1="${sig}" - extra_key2="sig2" - extra_value2="${sig}" - fi - set +e curl "${url}" \ -F "prod=${CHROMEOS_PRODUCT}" \ From c6a58e0f73a2e21941bbcba863eb78bc1c1ae0fb Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Mon, 1 Nov 2010 18:05:30 -0700 Subject: [PATCH 031/225] crash-reporter: add filtering to make crash tests resilient to crashy builds BUG=8415 TEST=Run UserCrash and CrashSender while an unrelated process is crashing at about 10Hz Change-Id: I7f86df8caec19278337ca463f09705e4fcd788f3 Review URL: http://codereview.chromium.org/4190014 --- crash_reporter/user_collector.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index f9cb49400..02025c20d 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -21,8 +21,12 @@ #include "crash-reporter/system_logging.h" #include "gflags/gflags.h" +#pragma GCC diagnostic ignored "-Wstrict-aliasing" DEFINE_bool(core2md_failure_test, false, "Core2md failure test"); DEFINE_bool(directory_failure_test, false, "Spool directory failure test"); +DEFINE_string(filter_in, "", + "Ignore all crashes but this for testing"); +#pragma GCC diagnostic error "-Wstrict-aliasing" static const char kCollectionErrorSignature[] = "crash_reporter-user-collection"; @@ -423,6 +427,19 @@ bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { // failing by indicating an unknown name. exec = "unknown"; } + + // Allow us to test the crash reporting mechanism successfully even if + // other parts of the system crash. + if (!FLAGS_filter_in.empty() && + (FLAGS_filter_in == "none" || + FLAGS_filter_in != exec)) { + // We use a different format message to make it more obvious in tests + // which crashes are test generated and which are real. + logger_->LogWarning("Ignoring crash from %s[%d] while filter_in=%s", + exec.c_str(), pid, FLAGS_filter_in.c_str()); + return true; + } + bool feedback = is_feedback_allowed_function_(); logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", exec.c_str(), pid, signal, From 9b346474538a255bc7144ae358bb0ee129163d58 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Sun, 7 Nov 2010 13:45:45 -0800 Subject: [PATCH 032/225] crash-reporter: Avoid writing through symlinks. BUG=7987 TEST=bvts Change-Id: I875adeb5073936e790beb93f6a223a1642131cbd Review URL: http://codereview.chromium.org/4603001 --- crash_reporter/crash_collector.cc | 79 ++++++++++++++- crash_reporter/crash_collector.h | 10 ++ crash_reporter/crash_collector_test.cc | 128 +++++++++++++++++++++++-- crash_reporter/kernel_collector.cc | 9 +- crash_reporter/user_collector.cc | 67 +------------ crash_reporter/user_collector.h | 3 - crash_reporter/user_collector_test.cc | 70 +------------- 7 files changed, 220 insertions(+), 146 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 1e2cd1abd..7abe2a580 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -5,11 +5,15 @@ #include "crash-reporter/crash_collector.h" #include +#include // For file creation modes. #include // For struct passwd. #include // for mode_t. +#include // For waitpid. +#include // For execv and fork. #include +#include "base/eintr_wrapper.h" #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" @@ -60,6 +64,76 @@ void CrashCollector::Initialize( logger_ = logger; } +int CrashCollector::WriteNewFile(const FilePath &filename, + const char *data, + int size) { + int fd = HANDLE_EINTR(open(filename.value().c_str(), + O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666)); + if (fd < 0) { + return -1; + } + + int rv = file_util::WriteFileDescriptor(fd, data, size); + HANDLE_EINTR(close(fd)); + return rv; +} + +int CrashCollector::ForkExecAndPipe(std::vector &arguments, + const char *output_file) { + // Copy off a writeable version of arguments. + scoped_array argv(new char *[arguments.size() + 1]); + int total_args_size = 0; + for (size_t i = 0; i < arguments.size(); ++i) { + if (arguments[i] == NULL) { + logger_->LogError("Bad parameter"); + return -1; + } + total_args_size += strlen(arguments[i]) + 1; + } + scoped_array buffer(new char[total_args_size]); + char *buffer_pointer = &buffer[0]; + + for (size_t i = 0; i < arguments.size(); ++i) { + argv[i] = buffer_pointer; + strcpy(buffer_pointer, arguments[i]); + buffer_pointer += strlen(arguments[i]); + *buffer_pointer = '\0'; + ++buffer_pointer; + } + argv[arguments.size()] = NULL; + + int pid = fork(); + if (pid < 0) { + logger_->LogError("Fork failed: %d", errno); + return -1; + } + + if (pid == 0) { + int output_handle = HANDLE_EINTR(creat(output_file, 0700)); + if (output_handle < 0) { + logger_->LogError("Could not create %s: %d", output_file, errno); + // Avoid exit() to avoid atexit handlers from parent. + _exit(127); + } + dup2(output_handle, 1); + dup2(output_handle, 2); + execv(argv[0], &argv[0]); + logger_->LogError("Exec failed: %d", errno); + _exit(127); + } + + int status = 0; + if (HANDLE_EINTR(waitpid(pid, &status, 0)) < 0) { + logger_->LogError("Problem waiting for pid: %d", errno); + return -1; + } + if (!WIFEXITED(status)) { + logger_->LogError("Process did not exit normally: %d", status); + return -1; + } + return WEXITSTATUS(status); +} + std::string CrashCollector::Sanitize(const std::string &name) { std::string result = name; for (size_t i = 0; i < name.size(); ++i) { @@ -291,7 +365,10 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, version.c_str(), payload_path.c_str(), payload_size); - if (!file_util::WriteFile(meta_path, meta_data.c_str(), meta_data.size())) { + // We must use WriteNewFile instead of file_util::WriteFile as we + // do not want to write with root access to a symlink that an attacker + // might have created. + if (WriteNewFile(meta_path, meta_data.c_str(), meta_data.size()) < 0) { logger_->LogError("Unable to write %s", meta_path.value().c_str()); } } diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index e5eddcdd5..219c8af6e 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -39,15 +39,25 @@ class CrashCollector { FRIEND_TEST(CrashCollectorTest, CheckHasCapacityUsual); FRIEND_TEST(CrashCollectorTest, GetCrashDirectoryInfo); FRIEND_TEST(CrashCollectorTest, GetCrashPath); + FRIEND_TEST(CrashCollectorTest, ForkExecAndPipe); FRIEND_TEST(CrashCollectorTest, FormatDumpBasename); FRIEND_TEST(CrashCollectorTest, Initialize); FRIEND_TEST(CrashCollectorTest, MetaData); FRIEND_TEST(CrashCollectorTest, ReadKeyValueFile); FRIEND_TEST(CrashCollectorTest, Sanitize); + FRIEND_TEST(CrashCollectorTest, WriteNewFile); // Set maximum enqueued crashes in a crash directory. static const int kMaxCrashDirectorySize; + // Writes |data| of |size| to |filename|, which must be a new file. + // If the file already exists or writing fails, return a negative value. + // Otherwise returns the number of bytes written. + int WriteNewFile(const FilePath &filename, const char *data, int size); + + int ForkExecAndPipe(std::vector &arguments, + const char *output_file); + // Return a filename that has only [a-z0-1_] characters by mapping // all others into '_'. std::string Sanitize(const std::string &name); diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 859653b13..bf3a9ddfe 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -8,9 +8,16 @@ #include "base/string_util.h" #include "crash-reporter/crash_collector.h" #include "crash-reporter/system_logging_mock.h" +#include "crash-reporter/test_helpers.h" #include "gflags/gflags.h" #include "gtest/gtest.h" +// This test assumes the following standard binaries are installed. +static const char kBinBash[] = "/bin/bash"; +static const char kBinCp[] = "/bin/cp"; +static const char kBinEcho[] = "/bin/echo"; +static const char kBinFalse[] = "/bin/false"; + void CountCrash() { ADD_FAILURE(); } @@ -48,6 +55,81 @@ TEST_F(CrashCollectorTest, Initialize) { ASSERT_TRUE(&logging_ == collector_.logger_); } +TEST_F(CrashCollectorTest, WriteNewFile) { + FilePath test_file = test_dir_.Append("test_new"); + const char kBuffer[] = "buffer"; + EXPECT_EQ(strlen(kBuffer), + collector_.WriteNewFile(test_file, + kBuffer, + strlen(kBuffer))); + EXPECT_LT(collector_.WriteNewFile(test_file, + kBuffer, + strlen(kBuffer)), 0); +} + +TEST_F(CrashCollectorTest, ForkExecAndPipe) { + std::vector args; + char output_file[] = "test/fork_out"; + + // Test basic call with stdout. + args.clear(); + args.push_back(kBinEcho); + args.push_back("hello world"); + EXPECT_EQ(0, collector_.ForkExecAndPipe(args, output_file)); + ExpectFileEquals("hello world\n", output_file); + EXPECT_EQ("", logging_.log()); + + // Test non-zero return value + logging_.clear(); + args.clear(); + args.push_back(kBinFalse); + EXPECT_EQ(1, collector_.ForkExecAndPipe(args, output_file)); + ExpectFileEquals("", output_file); + EXPECT_EQ("", logging_.log()); + + // Test bad output_file. + EXPECT_EQ(127, collector_.ForkExecAndPipe(args, "/bad/path")); + + // Test bad executable. + logging_.clear(); + args.clear(); + args.push_back("false"); + EXPECT_EQ(127, collector_.ForkExecAndPipe(args, output_file)); + + // Test stderr captured. + std::string contents; + logging_.clear(); + args.clear(); + args.push_back(kBinCp); + EXPECT_EQ(1, collector_.ForkExecAndPipe(args, output_file)); + EXPECT_TRUE(file_util::ReadFileToString(FilePath(output_file), + &contents)); + EXPECT_NE(std::string::npos, contents.find("missing file operand")); + EXPECT_EQ("", logging_.log()); + + // NULL parameter. + logging_.clear(); + args.clear(); + args.push_back(NULL); + EXPECT_EQ(-1, collector_.ForkExecAndPipe(args, output_file)); + EXPECT_NE(std::string::npos, + logging_.log().find("Bad parameter")); + + // No parameters. + args.clear(); + EXPECT_EQ(127, collector_.ForkExecAndPipe(args, output_file)); + + // Segmentation faulting process. + logging_.clear(); + args.clear(); + args.push_back(kBinBash); + args.push_back("-c"); + args.push_back("kill -SEGV $$"); + EXPECT_EQ(-1, collector_.ForkExecAndPipe(args, output_file)); + EXPECT_NE(std::string::npos, + logging_.log().find("Process did not exit normally")); +} + TEST_F(CrashCollectorTest, Sanitize) { EXPECT_EQ("chrome", collector_.Sanitize("chrome")); EXPECT_EQ("CHROME", collector_.Sanitize("CHROME")); @@ -231,7 +313,8 @@ TEST_F(CrashCollectorTest, ReadKeyValueFile) { } TEST_F(CrashCollectorTest, MetaData) { - FilePath meta_file = test_dir_.Append("generated.meta"); + const char kMetaFileBasename[] = "generated.meta"; + FilePath meta_file = test_dir_.Append(kMetaFileBasename); FilePath lsb_release = test_dir_.Append("lsb-release"); FilePath payload_file = test_dir_.Append("payload-file"); std::string contents; @@ -247,12 +330,43 @@ TEST_F(CrashCollectorTest, MetaData) { collector_.AddCrashMetaData("foo", "bar"); collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value()); EXPECT_TRUE(file_util::ReadFileToString(meta_file, &contents)); - EXPECT_EQ("foo=bar\n" - "exec_name=kernel\n" - "ver=version\n" - "payload=test/payload-file\n" - "payload_size=3\n" - "done=1\n", contents); + const char kExpectedMeta[] = + "foo=bar\n" + "exec_name=kernel\n" + "ver=version\n" + "payload=test/payload-file\n" + "payload_size=3\n" + "done=1\n"; + EXPECT_EQ(kExpectedMeta, contents); + + // Test target of symlink is not overwritten. + payload_file = test_dir_.Append("payload2-file"); + ASSERT_TRUE( + file_util::WriteFile(payload_file, + kPayload, strlen(kPayload))); + FilePath meta_symlink_path = test_dir_.Append("symlink.meta"); + ASSERT_EQ(0, + symlink(kMetaFileBasename, + meta_symlink_path.value().c_str())); + ASSERT_TRUE(file_util::PathExists(meta_symlink_path)); + logging_.clear(); + collector_.WriteCrashMetaData(meta_symlink_path, + "kernel", + payload_file.value()); + // Target metadata contents sould have stayed the same. + contents.clear(); + EXPECT_TRUE(file_util::ReadFileToString(meta_file, &contents)); + EXPECT_EQ(kExpectedMeta, contents); + EXPECT_NE(std::string::npos, logging_.log().find("Unable to write")); + + // Test target of dangling symlink is not created. + file_util::Delete(meta_file, false); + ASSERT_FALSE(file_util::PathExists(meta_file)); + logging_.clear(); + collector_.WriteCrashMetaData(meta_symlink_path, "kernel", + payload_file.value()); + EXPECT_FALSE(file_util::PathExists(meta_file)); + EXPECT_NE(std::string::npos, logging_.log().find("Unable to write")); } int main(int argc, char **argv) { diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index c95873347..4fa51e26e 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -277,9 +277,12 @@ bool KernelCollector::Collect() { FilePath kernel_crash_path = root_crash_directory.Append( StringPrintf("%s.kcrash", dump_basename.c_str())); - if (file_util::WriteFile(kernel_crash_path, - kernel_dump.data(), - kernel_dump.length()) != + // We must use WriteNewFile instead of file_util::WriteFile as we + // do not want to write with root access to a symlink that an attacker + // might have created. + if (WriteNewFile(kernel_crash_path, + kernel_dump.data(), + kernel_dump.length()) != static_cast(kernel_dump.length())) { logger_->LogInfo("Failed to write kernel dump to %s", kernel_crash_path.value().c_str()); diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 02025c20d..6fa368d05 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -4,17 +4,13 @@ #include "crash-reporter/user_collector.h" -#include // For creat. #include // For struct group. #include // For struct passwd. #include // For getpwuid_r, getgrnam_r, WEXITSTATUS. -#include // For waitpid. -#include // For execv and fork. #include #include -#include "base/eintr_wrapper.h" #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" @@ -176,9 +172,10 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid, std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); - file_util::WriteFile(log_path, - error_log_.data(), - error_log_.length()); + // We must use WriteNewFile instead of file_util::WriteFile as we do + // not want to write with root access to a symlink that an attacker + // might have created. + WriteNewFile(log_path, error_log_.data(), error_log_.length()); AddCrashMetaData("sig", kCollectionErrorSignature); WriteCrashMetaData(meta_path, exec, log_path.value()); } @@ -255,62 +252,6 @@ bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { return false; } -int UserCollector::ForkExecAndPipe(std::vector &arguments, - const char *output_file) { - // Copy off a writeable version of arguments. - scoped_array argv(new char *[arguments.size() + 1]); - int total_args_size = 0; - for (size_t i = 0; i < arguments.size(); ++i) { - if (arguments[i] == NULL) { - logger_->LogError("Bad parameter"); - return -1; - } - total_args_size += strlen(arguments[i]) + 1; - } - scoped_array buffer(new char[total_args_size]); - char *buffer_pointer = &buffer[0]; - - for (size_t i = 0; i < arguments.size(); ++i) { - argv[i] = buffer_pointer; - strcpy(buffer_pointer, arguments[i]); - buffer_pointer += strlen(arguments[i]); - *buffer_pointer = '\0'; - ++buffer_pointer; - } - argv[arguments.size()] = NULL; - - int pid = fork(); - if (pid < 0) { - logger_->LogError("Fork failed: %d", errno); - return -1; - } - - if (pid == 0) { - int output_handle = creat(output_file, 0700); - if (output_handle < 0) { - logger_->LogError("Could not create %s: %d", output_file, errno); - // Avoid exit() to avoid atexit handlers from parent. - _exit(127); - } - dup2(output_handle, 1); - dup2(output_handle, 2); - execv(argv[0], &argv[0]); - logger_->LogError("Exec failed: %d", errno); - _exit(127); - } - - int status = 0; - if (HANDLE_EINTR(waitpid(pid, &status, 0)) < 0) { - logger_->LogError("Problem waiting for pid: %d", errno); - return -1; - } - if (!WIFEXITED(status)) { - logger_->LogError("Process did not exit normally: %d", status); - return -1; - } - return WEXITSTATUS(status); -} - bool UserCollector::RunCoreToMinidump(const FilePath &core_path, const FilePath &procfs_directory, const FilePath &minidump_path, diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 2ed8e6f47..109bf0c72 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -52,7 +52,6 @@ class UserCollector : public CrashCollector { FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPath); FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPid); FRIEND_TEST(UserCollectorTest, CopyOffProcFilesOK); - FRIEND_TEST(UserCollectorTest, ForkExecAndPipe); FRIEND_TEST(UserCollectorTest, GetIdFromStatus); FRIEND_TEST(UserCollectorTest, GetProcessPath); FRIEND_TEST(UserCollectorTest, GetSymlinkTarget); @@ -95,8 +94,6 @@ class UserCollector : public CrashCollector { FilePath *crash_file_path, bool *out_of_capacity); bool CopyStdinToCoreFile(const FilePath &core_path); - int ForkExecAndPipe(std::vector &arguments, - const char *output_file); bool RunCoreToMinidump(const FilePath &core_path, const FilePath &procfs_directory, const FilePath &minidump_path, diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index db707f5ac..06ba9b8f5 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -7,6 +7,7 @@ #include "base/file_util.h" #include "crash-reporter/system_logging_mock.h" #include "crash-reporter/user_collector.h" +#include "crash-reporter/test_helpers.h" #include "gflags/gflags.h" #include "gtest/gtest.h" @@ -15,12 +16,6 @@ static bool s_metrics = false; static const char kFilePath[] = "/my/path"; -// This test assumes the following standard binaries are installed. -static const char kBinBash[] = "/bin/bash"; -static const char kBinCp[] = "/bin/cp"; -static const char kBinEcho[] = "/bin/echo"; -static const char kBinFalse[] = "/bin/false"; - void CountCrash() { ++s_crashes; } @@ -91,69 +86,6 @@ TEST_F(UserCollectorTest, DisableNoFileAccess) { std::string::npos); } -TEST_F(UserCollectorTest, ForkExecAndPipe) { - std::vector args; - char output_file[] = "test/fork_out"; - - // Test basic call with stdout. - args.clear(); - args.push_back(kBinEcho); - args.push_back("hello world"); - EXPECT_EQ(0, collector_.ForkExecAndPipe(args, output_file)); - ExpectFileEquals("hello world\n", output_file); - EXPECT_EQ("", logging_.log()); - - // Test non-zero return value - logging_.clear(); - args.clear(); - args.push_back(kBinFalse); - EXPECT_EQ(1, collector_.ForkExecAndPipe(args, output_file)); - ExpectFileEquals("", output_file); - EXPECT_EQ("", logging_.log()); - - // Test bad output_file. - EXPECT_EQ(127, collector_.ForkExecAndPipe(args, "/bad/path")); - - // Test bad executable. - logging_.clear(); - args.clear(); - args.push_back("false"); - EXPECT_EQ(127, collector_.ForkExecAndPipe(args, output_file)); - - // Test stderr captured. - std::string contents; - logging_.clear(); - args.clear(); - args.push_back(kBinCp); - EXPECT_EQ(1, collector_.ForkExecAndPipe(args, output_file)); - EXPECT_TRUE(file_util::ReadFileToString(FilePath(output_file), - &contents)); - EXPECT_NE(std::string::npos, contents.find("missing file operand")); - EXPECT_EQ("", logging_.log()); - - // NULL parameter. - logging_.clear(); - args.clear(); - args.push_back(NULL); - EXPECT_EQ(-1, collector_.ForkExecAndPipe(args, output_file)); - EXPECT_NE(std::string::npos, - logging_.log().find("Bad parameter")); - - // No parameters. - args.clear(); - EXPECT_EQ(127, collector_.ForkExecAndPipe(args, output_file)); - - // Segmentation faulting process. - logging_.clear(); - args.clear(); - args.push_back(kBinBash); - args.push_back("-c"); - args.push_back("kill -SEGV $$"); - EXPECT_EQ(-1, collector_.ForkExecAndPipe(args, output_file)); - EXPECT_NE(std::string::npos, - logging_.log().find("Process did not exit normally")); -} - TEST_F(UserCollectorTest, HandleCrashWithoutMetrics) { s_metrics = false; collector_.HandleCrash(10, 20, "foobar"); From a5118a97d5dc543e7ca58bef5d67cd22fe47f70a Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Sun, 7 Nov 2010 14:13:58 -0800 Subject: [PATCH 033/225] crash-reporter: add missing header file from last commit Change-Id: I85fd4f6b4eb851209e36d4ba803889abd00bf274 TBR=petkov BUG=7987 TEST=unittests pass Review URL: http://codereview.chromium.org/4649002 --- crash_reporter/test_helpers.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 crash_reporter/test_helpers.h diff --git a/crash_reporter/test_helpers.h b/crash_reporter/test_helpers.h new file mode 100644 index 000000000..b51590181 --- /dev/null +++ b/crash_reporter/test_helpers.h @@ -0,0 +1,18 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CRASH_REPORTER_TEST_HELPERS_H_ +#define _CRASH_REPORTER_TEST_HELPERS_H_ + +#include "gtest/gtest.h" + +inline void ExpectFileEquals(const char *golden, + const char *file_path) { + std::string contents; + EXPECT_TRUE(file_util::ReadFileToString(FilePath(file_path), + &contents)); + EXPECT_EQ(golden, contents); +} + +#endif // _CRASH_REPORTER_TEST_HELPERS_H_ From 2105b49796f2b753b44ccc792fd387dd0dddf54f Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Tue, 9 Nov 2010 16:14:38 -0800 Subject: [PATCH 034/225] crash-reporter: Ignore Chrome crashes, relying on Chrome breakpad Change-Id: I08e44d2c8b6be839d18efd2fe6e8b612a61ae723 BUG=8841 TEST=bvts Review URL: http://codereview.chromium.org/4499006 --- crash_reporter/user_collector.cc | 16 ++++++++++++++-- crash_reporter/user_collector_test.cc | 27 +++++++++++++++++++-------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 6fa368d05..f15c5783d 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -382,9 +382,21 @@ bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { } bool feedback = is_feedback_allowed_function_(); + const char *handling_string = "handling"; + if (!feedback) { + handling_string = "ignoring - no consent"; + } + + // Treat Chrome crashes as if the user opted-out. We stop counting Chrome + // crashes towards user crashes, so user crashes really mean non-Chrome + // user-space crashes. + if (exec == "chrome") { + feedback = false; + handling_string = "ignoring - chrome crash"; + } + logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", - exec.c_str(), pid, signal, - feedback ? "handling" : "ignoring - no consent"); + exec.c_str(), pid, signal, handling_string); if (feedback) { count_crash_function_(); diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 06ba9b8f5..faa168912 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -89,19 +89,30 @@ TEST_F(UserCollectorTest, DisableNoFileAccess) { TEST_F(UserCollectorTest, HandleCrashWithoutMetrics) { s_metrics = false; collector_.HandleCrash(10, 20, "foobar"); - ASSERT_NE(logging_.log().find( - "Received crash notification for foobar[20] sig 10"), - std::string::npos); + ASSERT_NE(std::string::npos, + logging_.log().find( + "Received crash notification for foobar[20] sig 10")); ASSERT_EQ(s_crashes, 0); } -TEST_F(UserCollectorTest, HandleCrashWithMetrics) { +TEST_F(UserCollectorTest, HandleNonChromeCrashWithMetrics) { + s_metrics = true; + collector_.HandleCrash(2, 5, "chromeos-wm"); + ASSERT_NE(std::string::npos, + logging_.log().find( + "Received crash notification for chromeos-wm[5] sig 2")); + ASSERT_EQ(s_crashes, 1); +} + +TEST_F(UserCollectorTest, HandleChromeCrashWithMetrics) { s_metrics = true; collector_.HandleCrash(2, 5, "chrome"); - ASSERT_NE(logging_.log().find( - "Received crash notification for chrome[5] sig 2"), - std::string::npos); - ASSERT_EQ(s_crashes, 1); + ASSERT_NE(std::string::npos, + logging_.log().find( + "Received crash notification for chrome[5] sig 2")); + ASSERT_NE(std::string::npos, + logging_.log().find("(ignoring - chrome crash)")); + ASSERT_EQ(s_crashes, 0); } TEST_F(UserCollectorTest, GetProcessPath) { From 814ef465a4ddf90f20cf796b90ae2264fee67e39 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Thu, 18 Nov 2010 17:29:32 -0800 Subject: [PATCH 035/225] crash-reporter: use HTTPS when sending crashes Change-Id: Ibca5616ba449dd1f96ae418a46b372d1a2f55412 BUG=9352 TEST=manual update and User/CrashSender Review URL: http://codereview.chromium.org/5092009 --- crash_reporter/crash_sender | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 2e56dec58..71792a952 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -46,7 +46,7 @@ MOCK_CRASH_SENDING="/tmp/mock-crash-sending" PAUSE_CRASH_SENDING="/var/lib/crash_sender_paused" # URL to send official build crash reports to. -REPORT_UPLOAD_PROD_URL="http://clients2.google.com/cr/report" +REPORT_UPLOAD_PROD_URL="https://clients2.google.com/cr/report" # File whose existence implies we're running and not to start again. RUN_FILE="/var/run/crash_sender.pid" @@ -61,6 +61,10 @@ TAG="$(basename $0)[$$]" # hours. TIMESTAMPS_DIR="/var/lib/crash_sender" +# Path to a directory of restricted certificates which includes +# a certificate for ${REPORT_UPLOAD_PROD_URL}. +RESTRICTED_CERTIFICATES_PATH="/usr/share/chromeos-ca-certificates" + # Temp directory for this process. TMP_DIR="" @@ -254,6 +258,7 @@ send_crash() { set +e curl "${url}" \ + --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \ -F "prod=${CHROMEOS_PRODUCT}" \ -F "ver=${chromeos_version}" \ -F "upload_file_${kind}=@${report_payload}" \ @@ -382,7 +387,8 @@ main() { check_not_already_running - for dependency in "${FIND}" "${METRICS_CLIENT}"; do + for dependency in "${FIND}" "${METRICS_CLIENT}" \ + "${RESTRICTED_CERTIFICATES_PATH}"; do if [ ! -x "${dependency}" ]; then lecho "Fatal: Crash sending disabled: ${dependency} not found." exit 1 From 3f7ed5d14aea9193e929ce4f3888f1471a3c7f87 Mon Sep 17 00:00:00 2001 From: Simon Que Date: Tue, 30 Nov 2010 17:10:54 -0800 Subject: [PATCH 036/225] Collect suspend and resume info from power manager Updated the unclean shutdown collector to check and report the modification times of trace files left by power manager to indicate that the system was suspended and that it was running on low battery. These files are interpreted by the unclean shutdown collector to determine the cause of an unclean shutdown -- either the battery ran out during suspend or the battery ran out after resuming from a low battery-induced suspend and it continued to run on battery. BUG=chromium-os:1472 TEST=Let the battery run below the cutoff, suspend & resume, and then remove the battery to simulate running dry; check crash reporter logs. Alternatively/additionally, let it suspend and remove the battery while suspended. Restart and check crash reporter logs. Signed-off-by: Simon Que Change-Id: I8e6767e8457afb7abf1e7300eac020adda1ebb48 Review URL: http://codereview.chromium.org/3644007 --- crash_reporter/unclean_shutdown_collector.cc | 44 +++++++++++++++++-- crash_reporter/unclean_shutdown_collector.h | 11 ++++- .../unclean_shutdown_collector_test.cc | 34 +++++++++++++- 3 files changed, 83 insertions(+), 6 deletions(-) diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc index bb2dd7a86..1e1c7842a 100644 --- a/crash_reporter/unclean_shutdown_collector.cc +++ b/crash_reporter/unclean_shutdown_collector.cc @@ -11,8 +11,18 @@ static const char kUncleanShutdownFile[] = "/var/lib/crash_reporter/pending_clean_shutdown"; +// Files created by power manager used for crash reporting. +static const char kPowerdTracePath[] = "/var/lib/power_manager"; +// Presence of this file indicates that the system was suspended +static const char kPowerdSuspended[] = "powerd_suspended"; +// Presence of this file indicates that the battery was critically low. +static const char kPowerdLowBattery[] = "powerd_low_battery"; + UncleanShutdownCollector::UncleanShutdownCollector() - : unclean_shutdown_file_(kUncleanShutdownFile) { + : unclean_shutdown_file_(kUncleanShutdownFile), + powerd_trace_path_(kPowerdTracePath), + powerd_suspended_file_(powerd_trace_path_.Append(kPowerdSuspended)), + powerd_low_battery_file_(powerd_trace_path_.Append(kPowerdLowBattery)) { } UncleanShutdownCollector::~UncleanShutdownCollector() { @@ -28,12 +38,15 @@ bool UncleanShutdownCollector::Enable() { return true; } -bool UncleanShutdownCollector::DeleteUncleanShutdownFile() { +bool UncleanShutdownCollector::DeleteUncleanShutdownFiles() { if (!file_util::Delete(FilePath(unclean_shutdown_file_), false)) { logger_->LogError("Failed to delete unclean shutdown file %s", unclean_shutdown_file_); return false; } + // Delete power manager trace files if they exist. + file_util::Delete(powerd_suspended_file_, false); + file_util::Delete(powerd_low_battery_file_, false); return true; } @@ -43,7 +56,11 @@ bool UncleanShutdownCollector::Collect() { return false; } logger_->LogWarning("Last shutdown was not clean"); - DeleteUncleanShutdownFile(); + if (DeadBatteryCausedUncleanShutdown()) { + DeleteUncleanShutdownFiles(); + return false; + } + DeleteUncleanShutdownFiles(); if (is_feedback_allowed_function_()) { count_crash_function_(); @@ -53,5 +70,24 @@ bool UncleanShutdownCollector::Collect() { bool UncleanShutdownCollector::Disable() { logger_->LogInfo("Clean shutdown signalled"); - return DeleteUncleanShutdownFile(); + return DeleteUncleanShutdownFiles(); +} + +bool UncleanShutdownCollector::DeadBatteryCausedUncleanShutdown() +{ + // Check for case of battery running out while suspended. + if (file_util::PathExists(powerd_suspended_file_)) { + logger_->LogInfo("Unclean shutdown occurred while suspended. Not counting " + "toward unclean shutdown statistic."); + return true; + } + // Check for case of battery running out after resuming from a low-battery + // suspend. + if (file_util::PathExists(powerd_low_battery_file_)) { + logger_->LogInfo("Unclean shutdown occurred while running with battery " + "critically low. Not counting toward unclean shutdown " + "statistic."); + return true; + } + return false; } diff --git a/crash_reporter/unclean_shutdown_collector.h b/crash_reporter/unclean_shutdown_collector.h index e7d8cef95..7e10e7f28 100644 --- a/crash_reporter/unclean_shutdown_collector.h +++ b/crash_reporter/unclean_shutdown_collector.h @@ -30,10 +30,19 @@ class UncleanShutdownCollector : public CrashCollector { private: friend class UncleanShutdownCollectorTest; FRIEND_TEST(UncleanShutdownCollectorTest, EnableCannotWrite); + FRIEND_TEST(UncleanShutdownCollectorTest, CollectDeadBatteryRunningLow); + FRIEND_TEST(UncleanShutdownCollectorTest, CollectDeadBatterySuspended); - bool DeleteUncleanShutdownFile(); + bool DeleteUncleanShutdownFiles(); + + // Check for unclean shutdown due to battery running out by analyzing powerd + // trace files. + bool DeadBatteryCausedUncleanShutdown(); const char *unclean_shutdown_file_; + FilePath powerd_trace_path_; + FilePath powerd_suspended_file_; + FilePath powerd_low_battery_file_; }; #endif // _CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_ diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc index 7be52af14..2cf9fb5d6 100644 --- a/crash_reporter/unclean_shutdown_collector_test.cc +++ b/crash_reporter/unclean_shutdown_collector_test.cc @@ -12,8 +12,10 @@ #include "gtest/gtest.h" static int s_crashes = 0; -static bool s_metrics = false; +static bool s_metrics = true; +static const char kTestLowBattery[] = "test/low_battery"; +static const char kTestSuspended[] = "test/suspended"; static const char kTestUnclean[] = "test/unclean"; void CountCrash() { @@ -34,6 +36,9 @@ class UncleanShutdownCollectorTest : public ::testing::Test { test_unclean_ = FilePath(kTestUnclean); collector_.unclean_shutdown_file_ = kTestUnclean; file_util::Delete(test_unclean_, true); + // Set up alternate power manager tracing files as well + collector_.powerd_suspended_file_ = FilePath(kTestSuspended); + collector_.powerd_low_battery_file_ = FilePath(kTestLowBattery); } protected: void WriteStringToFile(const FilePath &file_path, @@ -70,12 +75,39 @@ TEST_F(UncleanShutdownCollectorTest, CollectTrue) { ASSERT_TRUE(file_util::PathExists(test_unclean_)); ASSERT_TRUE(collector_.Collect()); ASSERT_FALSE(file_util::PathExists(test_unclean_)); + ASSERT_EQ(1, s_crashes); ASSERT_NE(std::string::npos, logging_.log().find("Last shutdown was not clean")); } TEST_F(UncleanShutdownCollectorTest, CollectFalse) { ASSERT_FALSE(collector_.Collect()); + ASSERT_EQ(0, s_crashes); +} + +TEST_F(UncleanShutdownCollectorTest, CollectDeadBatteryRunningLow) { + ASSERT_TRUE(collector_.Enable()); + ASSERT_TRUE(file_util::PathExists(test_unclean_)); + file_util::WriteFile(collector_.powerd_low_battery_file_, "", 0); + ASSERT_FALSE(collector_.Collect()); + ASSERT_FALSE(file_util::PathExists(test_unclean_)); + ASSERT_FALSE(file_util::PathExists(collector_.powerd_low_battery_file_)); + ASSERT_EQ(0, s_crashes); + ASSERT_NE(std::string::npos, + logging_.log().find("Unclean shutdown occurred while running with " + "battery critically low.")); +} + +TEST_F(UncleanShutdownCollectorTest, CollectDeadBatterySuspended) { + ASSERT_TRUE(collector_.Enable()); + ASSERT_TRUE(file_util::PathExists(test_unclean_)); + file_util::WriteFile(collector_.powerd_suspended_file_, "", 0); + ASSERT_FALSE(collector_.Collect()); + ASSERT_FALSE(file_util::PathExists(test_unclean_)); + ASSERT_FALSE(file_util::PathExists(collector_.powerd_suspended_file_)); + ASSERT_EQ(0, s_crashes); + ASSERT_NE(std::string::npos, + logging_.log().find("Unclean shutdown occurred while suspended.")); } TEST_F(UncleanShutdownCollectorTest, Disable) { From c49dbd4775986f32b2f09659595f9f28ef7f6b44 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Tue, 14 Dec 2010 17:44:11 -0800 Subject: [PATCH 037/225] crash-reporter: Capture and send recent update_engine logs when it crashes We also disallow more than 4 nested crashes. This way we know if core2md crashes for instance, but not if crash_reporter crashes while handling the core2md crash that came from crash_reporter. Note that the kernel does not know about parent/child process relationships, it just counts concurrent handling, so it is possible that some of many crashing processes will be discarded. See crash report id 473c22ed428d33a9 for an example report with update_engine logs. Change-Id: I9ff9f03a94dc87d2d42840511c2e5e42ee37cea8 BUG=9176 TEST=UserCrash,CrashSender,unit tests Review URL: http://codereview.chromium.org/5814001 --- crash_reporter/crash_collector.cc | 39 ++++++++++++++++++++++++- crash_reporter/crash_collector.h | 11 +++++++ crash_reporter/crash_collector_test.cc | 33 +++++++++++++++++++++ crash_reporter/crash_reporter_logs.conf | 22 ++++++++++++++ crash_reporter/crash_sender | 5 ++++ crash_reporter/user_collector.cc | 18 ++++++++++++ crash_reporter/user_collector.h | 6 ++++ crash_reporter/user_collector_test.cc | 18 +++++++++++- 8 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 crash_reporter/crash_reporter_logs.conf diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 7abe2a580..89fe41de4 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -21,6 +21,7 @@ static const char kDefaultUserName[] = "chronos"; static const char kLsbRelease[] = "/etc/lsb-release"; +static const char kShellPath[] = "/bin/sh"; static const char kSystemCrashPath[] = "/var/spool/crash"; static const char kUserCrashPath[] = "/home/chronos/user/crash"; @@ -109,7 +110,7 @@ int CrashCollector::ForkExecAndPipe(std::vector &arguments, } if (pid == 0) { - int output_handle = HANDLE_EINTR(creat(output_file, 0700)); + int output_handle = HANDLE_EINTR(creat(output_file, 0600)); if (output_handle < 0) { logger_->LogError("Could not create %s: %d", output_file, errno); // Avoid exit() to avoid atexit handlers from parent. @@ -307,6 +308,11 @@ bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) { return !full; } +bool CrashCollector::IsCommentLine(const std::string &line) { + size_t found = line.find_first_not_of(" "); + return found != std::string::npos && line[found] == '#'; +} + bool CrashCollector::ReadKeyValueFile( const FilePath &path, const char separator, @@ -324,6 +330,9 @@ bool CrashCollector::ReadKeyValueFile( // Allow empty strings. if (line->empty()) continue; + // Allow comment lines. + if (IsCommentLine(*line)) + continue; StringVector sides; SplitString(*line, separator, &sides); if (sides.size() != 2) { @@ -335,6 +344,34 @@ bool CrashCollector::ReadKeyValueFile( return !any_errors; } +bool CrashCollector::GetLogContents(const FilePath &config_path, + const std::string &exec_name, + const FilePath &output_file) { + std::map log_commands; + if (!ReadKeyValueFile(config_path, ':', &log_commands)) { + logger_->LogInfo("Unable to read log configuration file %s", + config_path.value().c_str()); + return false; + } + + if (log_commands.find(exec_name) == log_commands.end()) + return false; + + std::vector command; + command.push_back(kShellPath); + command.push_back("-c"); + std::string shell_command = log_commands[exec_name]; + command.push_back(shell_command.c_str()); + + int fork_result = ForkExecAndPipe(command, output_file.value().c_str()); + if (fork_result != 0) { + logger_->LogInfo("Running shell command %s failed with: %d", + shell_command.c_str(), fork_result); + return false; + } + return true; +} + void CrashCollector::AddCrashMetaData(const std::string &key, const std::string &value) { extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str())); diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 219c8af6e..772de24b7 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -39,9 +39,11 @@ class CrashCollector { FRIEND_TEST(CrashCollectorTest, CheckHasCapacityUsual); FRIEND_TEST(CrashCollectorTest, GetCrashDirectoryInfo); FRIEND_TEST(CrashCollectorTest, GetCrashPath); + FRIEND_TEST(CrashCollectorTest, GetLogContents); FRIEND_TEST(CrashCollectorTest, ForkExecAndPipe); FRIEND_TEST(CrashCollectorTest, FormatDumpBasename); FRIEND_TEST(CrashCollectorTest, Initialize); + FRIEND_TEST(CrashCollectorTest, IsCommentLine); FRIEND_TEST(CrashCollectorTest, MetaData); FRIEND_TEST(CrashCollectorTest, ReadKeyValueFile); FRIEND_TEST(CrashCollectorTest, Sanitize); @@ -102,12 +104,21 @@ class CrashCollector { // crash. bool CheckHasCapacity(const FilePath &crash_directory); + // Checks if the line starts with '#' after optional whitespace. + static bool IsCommentLine(const std::string &line); + // Read the given file of form [\n...] and return // a map of its contents. bool ReadKeyValueFile(const FilePath &file, char separator, std::map *dictionary); + // Write a log applicable to |exec_name| to |output_file| based on the + // log configuration file at |config_path|. + bool GetLogContents(const FilePath &config_path, + const std::string &exec_name, + const FilePath &output_file); + // Add non-standard meta data to the crash metadata file. Call // before calling WriteCrashMetaData. Key must not contain "=" or // "\n" characters. Value must not contain "\n" characters. diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index bf3a9ddfe..7f1d4c230 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -271,6 +271,15 @@ TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) { EXPECT_FALSE(CheckHasCapacity()); } +TEST_F(CrashCollectorTest, IsCommentLine) { + EXPECT_FALSE(CrashCollector::IsCommentLine("")); + EXPECT_TRUE(CrashCollector::IsCommentLine("#")); + EXPECT_TRUE(CrashCollector::IsCommentLine("#real comment")); + EXPECT_TRUE(CrashCollector::IsCommentLine(" # real comment")); + EXPECT_FALSE(CrashCollector::IsCommentLine("not comment")); + EXPECT_FALSE(CrashCollector::IsCommentLine(" not comment")); +} + TEST_F(CrashCollectorTest, ReadKeyValueFile) { const char *contents = ("a=b\n" "\n" @@ -294,10 +303,13 @@ TEST_F(CrashCollectorTest, ReadKeyValueFile) { " f g = h\n" "i=j\n" "=k\n" + "#comment=0\n" "l=\n"); file_util::WriteFile(path, contents, strlen(contents)); EXPECT_FALSE(collector_.ReadKeyValueFile(path, '=', &dictionary)); + EXPECT_EQ(5, dictionary.size()); + i = dictionary.find("a"); EXPECT_TRUE(i != dictionary.end() && i->second == "b c d"); i = dictionary.find("e"); @@ -369,6 +381,27 @@ TEST_F(CrashCollectorTest, MetaData) { EXPECT_NE(std::string::npos, logging_.log().find("Unable to write")); } +TEST_F(CrashCollectorTest, GetLogContents) { + FilePath config_file = test_dir_.Append("crash_config"); + FilePath output_file = test_dir_.Append("crash_log"); + const char kConfigContents[] = + "foobar:echo hello there | sed -e \"s/there/world/\""; + ASSERT_TRUE( + file_util::WriteFile(config_file, + kConfigContents, strlen(kConfigContents))); + EXPECT_FALSE(collector_.GetLogContents(config_file, + "barfoo", + output_file)); + EXPECT_FALSE(file_util::PathExists(output_file)); + EXPECT_TRUE(collector_.GetLogContents(config_file, + "foobar", + output_file)); + ASSERT_TRUE(file_util::PathExists(output_file)); + std::string contents; + EXPECT_TRUE(file_util::ReadFileToString(output_file, &contents)); + EXPECT_EQ("hello world\n", contents); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf new file mode 100644 index 000000000..5ae82a21d --- /dev/null +++ b/crash_reporter/crash_reporter_logs.conf @@ -0,0 +1,22 @@ +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can +# be found in the LICENSE file. + +# This file has the format: +# :\n +# +# Where when any executable with the basename crashes, the +# given is executed and its standard output and +# standard error is sent along with the crash report. +# +# Use caution in modifying this file. Only run common unix commands +# here as these commands will be run when a crash has recently +# occurred and we should avoid running anything that might cause +# another crash. Similarly these command block the notification of +# the crash to parent processes, so commands should execute quickly. +# +update_engine:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/update_engine/.) | tail -c 50000 + +# The following rules are only for testing purposes. +crash_log_test:echo hello world +crash_log_recursion_test:sleep 1 && /home/autotest/tests/crash_log_recursion_test diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 71792a952..10fdf37bf 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -209,6 +209,7 @@ send_crash() { local board="$(get_board)" local hwclass="$(get_hardware_class)" local write_payload_size="$(get_key_value "${meta_path}" "payload_size")" + local log="$(get_key_value "${meta_path}" "log")" local sig="$(get_key_value "${meta_path}" "sig")" local send_payload_size="$(stat --printf=%s "${report_payload}" 2>/dev/null)" @@ -221,6 +222,10 @@ send_crash() { extra_value1="${sig}" extra_key2="sig2" extra_value2="${sig}" + elif [ "${log}" != "undefined" ]; then + # Upload a log file if it was specified. + extra_key1="log" + extra_value1="@${log}" fi lecho "Sending crash:" diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index f15c5783d..59b460bdc 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -30,15 +30,22 @@ static const char kCollectionErrorSignature[] = // instead pipe the core file into a user space process. See // core(5) man page. static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern"; +static const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit"; +// Set core_pipe_limit to 4 so that we can catch a few unrelated concurrent +// crashes, but finite to avoid infinitely recursing on crash handling. +static const char kCorePipeLimit[] = "4"; static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; static const char kLeaveCoreFile[] = "/root/.leave_core"; +static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf"; + const char *UserCollector::kUserId = "Uid:\t"; const char *UserCollector::kGroupId = "Gid:\t"; UserCollector::UserCollector() : generate_diagnostics_(false), core_pattern_file_(kCorePatternFile), + core_pipe_limit_file_(kCorePipeLimitFile), initialized_(false) { } @@ -71,6 +78,13 @@ bool UserCollector::SetUpInternal(bool enabled) { CHECK(initialized_); logger_->LogInfo("%s user crash handling", enabled ? "Enabling" : "Disabling"); + if (file_util::WriteFile(FilePath(core_pipe_limit_file_), + kCorePipeLimit, + strlen(kCorePipeLimit)) != + static_cast(strlen(kCorePipeLimit))) { + logger_->LogError("Unable to write %s", core_pipe_limit_file_.c_str()); + return false; + } std::string pattern = GetPattern(enabled); if (file_util::WriteFile(FilePath(core_pattern_file_), pattern.c_str(), @@ -332,6 +346,10 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid, FilePath core_path = GetCrashPath(crash_path, dump_basename, "core"); FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp"); + FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); + + if (GetLogContents(FilePath(kDefaultLogConfig), exec, log_path)) + AddCrashMetaData("log", log_path.value()); if (!ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path)) { diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 109bf0c72..90bc578ee 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -47,6 +47,11 @@ class UserCollector : public CrashCollector { core_pattern_file_ = pattern; } + // Set (override the default) core pipe limit file. + void set_core_pipe_limit_file(const std::string &path) { + core_pipe_limit_file_ = path; + } + private: friend class UserCollectorTest; FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPath); @@ -107,6 +112,7 @@ class UserCollector : public CrashCollector { bool generate_diagnostics_; std::string core_pattern_file_; + std::string core_pipe_limit_file_; std::string our_path_; bool initialized_; diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index faa168912..942123fcb 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -32,8 +32,10 @@ class UserCollectorTest : public ::testing::Test { IsMetrics, &logging_, false); + file_util::Delete(FilePath("test"), true); mkdir("test", 0777); collector_.set_core_pattern_file("test/core_pattern"); + collector_.set_core_pipe_limit_file("test/core_pipe_limit"); pid_ = getpid(); } protected: @@ -53,12 +55,13 @@ class UserCollectorTest : public ::testing::Test { TEST_F(UserCollectorTest, EnableOK) { ASSERT_TRUE(collector_.Enable()); ExpectFileEquals("|/my/path --signal=%s --pid=%p", "test/core_pattern"); + ExpectFileEquals("4", "test/core_pipe_limit"); ASSERT_EQ(s_crashes, 0); ASSERT_NE(logging_.log().find("Enabling user crash handling"), std::string::npos); } -TEST_F(UserCollectorTest, EnableNoFileAccess) { +TEST_F(UserCollectorTest, EnableNoPatternFileAccess) { collector_.set_core_pattern_file("/does_not_exist"); ASSERT_FALSE(collector_.Enable()); ASSERT_EQ(s_crashes, 0); @@ -68,6 +71,19 @@ TEST_F(UserCollectorTest, EnableNoFileAccess) { std::string::npos); } +TEST_F(UserCollectorTest, EnableNoPipeLimitFileAccess) { + collector_.set_core_pipe_limit_file("/does_not_exist"); + ASSERT_FALSE(collector_.Enable()); + ASSERT_EQ(s_crashes, 0); + // Core pattern should not be written if we cannot access the pipe limit + // or otherwise we may set a pattern that results in infinite recursion. + ASSERT_FALSE(file_util::PathExists(FilePath("test/core_pattern"))); + ASSERT_NE(logging_.log().find("Enabling user crash handling"), + std::string::npos); + ASSERT_NE(logging_.log().find("Unable to write /does_not_exist"), + std::string::npos); +} + TEST_F(UserCollectorTest, DisableOK) { ASSERT_TRUE(collector_.Disable()); ExpectFileEquals("core", "test/core_pattern"); From c4e01cc146d67d8bb7e2c2764eb3b9403873ed3f Mon Sep 17 00:00:00 2001 From: Dale Curtis Date: Mon, 10 Jan 2011 11:49:50 -0800 Subject: [PATCH 038/225] Change path from /home/autotest to /usr/local/autotest. Additional change for the relocation of Autotest from /home/autotest to /usr/local/autotest. See http://codereview.chromium.org/6090004/ Change-Id: I2995fdc76f56d4efbb6450d4a7da3adc321eae91 BUG=chromium-os:10799 TEST=Will test as whole with rest of /usr/local changes. Review URL: http://codereview.chromium.org/5964016 --- crash_reporter/crash_reporter_logs.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 5ae82a21d..172b9c38c 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -19,4 +19,4 @@ update_engine:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/ # The following rules are only for testing purposes. crash_log_test:echo hello world -crash_log_recursion_test:sleep 1 && /home/autotest/tests/crash_log_recursion_test +crash_log_recursion_test:sleep 1 && /usr/local/autotest/tests/crash_log_recursion_test From 1d3c3040836f7072bd005177291a0c29590b1e77 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Sat, 22 Jan 2011 06:16:40 -0800 Subject: [PATCH 039/225] crash-reporter: Send systemwide stability information up to Chrome Change-Id: Idcb3dff3b98951b41a55b9f48968c3608b44efae BUG=9352 TEST= 1) UserCrash 2) Crash random process and verify Chrome indicates "other user" crashes occurred in its stability UMA data. Review URL: http://codereview.chromium.org/6138005 --- crash_reporter/crash_reporter.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 07dcd9c5c..c583dec7a 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -53,21 +53,30 @@ static bool TouchFile(const FilePath &file_path) { } static void CountKernelCrash() { + // TODO(kmixter): We can remove this histogram as part of + // crosbug.com/11163. s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), kCrashKindKernel, kCrashKindMax); + s_metrics_lib.SendCrashToUMA("kernel"); } static void CountUncleanShutdown() { + // TODO(kmixter): We can remove this histogram as part of + // crosbug.com/11163. s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), kCrashKindUncleanShutdown, kCrashKindMax); + s_metrics_lib.SendCrashToUMA("uncleanshutdown"); } static void CountUserCrash() { + // TODO(kmixter): We can remove this histogram as part of + // crosbug.com/11163. s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), kCrashKindUser, kCrashKindMax); + s_metrics_lib.SendCrashToUMA("user"); std::string command = StringPrintf( "/usr/bin/dbus-send --type=signal --system / \"%s\" &", kUserCrashSignal); From 1b8fe015585c0b966d5240b8e22eba1af8c69e88 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Tue, 25 Jan 2011 13:33:05 -0800 Subject: [PATCH 040/225] crash-reporter: Add diagnostics to help diagnose failures in the wild We add logging messages that are generated during invocation of core2md, but we also enable sending arbitrary system info based on the configuration file. In this case we add the list of running processes, meminfo, and dmesg. Change-Id: Ifdf29b89dd60d56349fa39095d2aa07f6b5e2de2 BUG=chromium-os:6299,chromium-os:9345 TEST=UserCrash, unit tests Review URL: http://codereview.chromium.org/6297004 --- crash_reporter/crash_collector.cc | 3 +- crash_reporter/crash_collector.h | 9 ++ crash_reporter/crash_collector_test.cc | 145 ++++++++++++++---------- crash_reporter/crash_reporter.cc | 8 +- crash_reporter/crash_reporter_logs.conf | 5 + crash_reporter/system_logging.cc | 23 +++- crash_reporter/system_logging.h | 15 +++ crash_reporter/system_logging_mock.cc | 7 ++ crash_reporter/system_logging_mock.h | 2 + crash_reporter/user_collector.cc | 112 ++++++++++++------ crash_reporter/user_collector.h | 10 +- crash_reporter/user_collector_test.cc | 35 +++++- 12 files changed, 255 insertions(+), 119 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 89fe41de4..afd72d1e1 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -110,7 +110,8 @@ int CrashCollector::ForkExecAndPipe(std::vector &arguments, } if (pid == 0) { - int output_handle = HANDLE_EINTR(creat(output_file, 0600)); + int output_handle = HANDLE_EINTR( + open(output_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666)); if (output_handle < 0) { logger_->LogError("Could not create %s: %d", output_file, errno); // Avoid exit() to avoid atexit handlers from parent. diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 772de24b7..f3fcbe596 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -48,6 +48,15 @@ class CrashCollector { FRIEND_TEST(CrashCollectorTest, ReadKeyValueFile); FRIEND_TEST(CrashCollectorTest, Sanitize); FRIEND_TEST(CrashCollectorTest, WriteNewFile); + FRIEND_TEST(ForkExecAndPipeTest, Basic); + FRIEND_TEST(ForkExecAndPipeTest, NonZeroReturnValue); + FRIEND_TEST(ForkExecAndPipeTest, BadOutputFile); + FRIEND_TEST(ForkExecAndPipeTest, ExistingOutputFile); + FRIEND_TEST(ForkExecAndPipeTest, BadExecutable); + FRIEND_TEST(ForkExecAndPipeTest, StderrCaptured); + FRIEND_TEST(ForkExecAndPipeTest, NULLParam); + FRIEND_TEST(ForkExecAndPipeTest, NoParams); + FRIEND_TEST(ForkExecAndPipeTest, SegFaultHandling); // Set maximum enqueued crashes in a crash directory. static const int kMaxCrashDirectorySize; diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 7f1d4c230..8100a5f54 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -67,69 +67,6 @@ TEST_F(CrashCollectorTest, WriteNewFile) { strlen(kBuffer)), 0); } -TEST_F(CrashCollectorTest, ForkExecAndPipe) { - std::vector args; - char output_file[] = "test/fork_out"; - - // Test basic call with stdout. - args.clear(); - args.push_back(kBinEcho); - args.push_back("hello world"); - EXPECT_EQ(0, collector_.ForkExecAndPipe(args, output_file)); - ExpectFileEquals("hello world\n", output_file); - EXPECT_EQ("", logging_.log()); - - // Test non-zero return value - logging_.clear(); - args.clear(); - args.push_back(kBinFalse); - EXPECT_EQ(1, collector_.ForkExecAndPipe(args, output_file)); - ExpectFileEquals("", output_file); - EXPECT_EQ("", logging_.log()); - - // Test bad output_file. - EXPECT_EQ(127, collector_.ForkExecAndPipe(args, "/bad/path")); - - // Test bad executable. - logging_.clear(); - args.clear(); - args.push_back("false"); - EXPECT_EQ(127, collector_.ForkExecAndPipe(args, output_file)); - - // Test stderr captured. - std::string contents; - logging_.clear(); - args.clear(); - args.push_back(kBinCp); - EXPECT_EQ(1, collector_.ForkExecAndPipe(args, output_file)); - EXPECT_TRUE(file_util::ReadFileToString(FilePath(output_file), - &contents)); - EXPECT_NE(std::string::npos, contents.find("missing file operand")); - EXPECT_EQ("", logging_.log()); - - // NULL parameter. - logging_.clear(); - args.clear(); - args.push_back(NULL); - EXPECT_EQ(-1, collector_.ForkExecAndPipe(args, output_file)); - EXPECT_NE(std::string::npos, - logging_.log().find("Bad parameter")); - - // No parameters. - args.clear(); - EXPECT_EQ(127, collector_.ForkExecAndPipe(args, output_file)); - - // Segmentation faulting process. - logging_.clear(); - args.clear(); - args.push_back(kBinBash); - args.push_back("-c"); - args.push_back("kill -SEGV $$"); - EXPECT_EQ(-1, collector_.ForkExecAndPipe(args, output_file)); - EXPECT_NE(std::string::npos, - logging_.log().find("Process did not exit normally")); -} - TEST_F(CrashCollectorTest, Sanitize) { EXPECT_EQ("chrome", collector_.Sanitize("chrome")); EXPECT_EQ("CHROME", collector_.Sanitize("CHROME")); @@ -389,10 +326,12 @@ TEST_F(CrashCollectorTest, GetLogContents) { ASSERT_TRUE( file_util::WriteFile(config_file, kConfigContents, strlen(kConfigContents))); + file_util::Delete(FilePath(output_file), false); EXPECT_FALSE(collector_.GetLogContents(config_file, "barfoo", output_file)); EXPECT_FALSE(file_util::PathExists(output_file)); + file_util::Delete(FilePath(output_file), false); EXPECT_TRUE(collector_.GetLogContents(config_file, "foobar", output_file)); @@ -402,6 +341,86 @@ TEST_F(CrashCollectorTest, GetLogContents) { EXPECT_EQ("hello world\n", contents); } +class ForkExecAndPipeTest : public CrashCollectorTest { + public: + void SetUp() { + CrashCollectorTest::SetUp(); + output_file_ = "test/fork_out"; + file_util::Delete(FilePath(output_file_), false); + } + + void TearDown() { + CrashCollectorTest::TearDown(); + } + + protected: + std::vector args_; + const char *output_file_; +}; + +TEST_F(ForkExecAndPipeTest, Basic) { + args_.push_back(kBinEcho); + args_.push_back("hello world"); + EXPECT_EQ(0, collector_.ForkExecAndPipe(args_, output_file_)); + ExpectFileEquals("hello world\n", output_file_); + EXPECT_EQ("", logging_.log()); +} + +TEST_F(ForkExecAndPipeTest, NonZeroReturnValue) { + args_.push_back(kBinFalse); + EXPECT_EQ(1, collector_.ForkExecAndPipe(args_, output_file_)); + ExpectFileEquals("", output_file_); + EXPECT_EQ("", logging_.log()); +} + +TEST_F(ForkExecAndPipeTest, BadOutputFile) { + EXPECT_EQ(127, collector_.ForkExecAndPipe(args_, "/bad/path")); +} + +TEST_F(ForkExecAndPipeTest, ExistingOutputFile) { + args_.push_back(kBinEcho); + args_.push_back("hello world"); + EXPECT_FALSE(file_util::PathExists(FilePath(output_file_))); + EXPECT_EQ(0, collector_.ForkExecAndPipe(args_, output_file_)); + EXPECT_TRUE(file_util::PathExists(FilePath(output_file_))); + EXPECT_EQ(127, collector_.ForkExecAndPipe(args_, output_file_)); +} + +TEST_F(ForkExecAndPipeTest, BadExecutable) { + args_.push_back("false"); + EXPECT_EQ(127, collector_.ForkExecAndPipe(args_, output_file_)); +} + +TEST_F(ForkExecAndPipeTest, StderrCaptured) { + std::string contents; + args_.push_back(kBinCp); + EXPECT_EQ(1, collector_.ForkExecAndPipe(args_, output_file_)); + EXPECT_TRUE(file_util::ReadFileToString(FilePath(output_file_), + &contents)); + EXPECT_NE(std::string::npos, contents.find("missing file operand")); + EXPECT_EQ("", logging_.log()); +} + +TEST_F(ForkExecAndPipeTest, NULLParam) { + args_.push_back(NULL); + EXPECT_EQ(-1, collector_.ForkExecAndPipe(args_, output_file_)); + EXPECT_NE(std::string::npos, + logging_.log().find("Bad parameter")); +} + +TEST_F(ForkExecAndPipeTest, NoParams) { + EXPECT_EQ(127, collector_.ForkExecAndPipe(args_, output_file_)); +} + +TEST_F(ForkExecAndPipeTest, SegFaultHandling) { + args_.push_back(kBinBash); + args_.push_back("-c"); + args_.push_back("kill -SEGV $$"); + EXPECT_EQ(-1, collector_.ForkExecAndPipe(args_, output_file_)); + EXPECT_NE(std::string::npos, + logging_.log().find("Process did not exit normally")); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index c583dec7a..ffe6cdfed 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -21,8 +21,7 @@ DEFINE_bool(clean_shutdown, false, "Signal clean shutdown"); DEFINE_string(generate_kernel_signature, "", "Generate signature from given kcrash file"); DEFINE_bool(crash_test, false, "Crash test"); -DEFINE_int32(pid, -1, "Crashing PID"); -DEFINE_int32(signal, -1, "Signal causing crash"); +DEFINE_string(user, "", "User crash info (pid:signal:exec_name)"); DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); #pragma GCC diagnostic error "-Wstrict-aliasing" @@ -140,8 +139,7 @@ static int Initialize(KernelCollector *kernel_collector, static int HandleUserCrash(UserCollector *user_collector) { // Handle a specific user space crash. - CHECK(FLAGS_signal != -1) << "Signal must be set"; - CHECK(FLAGS_pid != -1) << "PID must be set"; + CHECK(!FLAGS_user.empty()) << "--user= must be set"; // Make it possible to test what happens when we crash while // handling a crash. @@ -151,7 +149,7 @@ static int HandleUserCrash(UserCollector *user_collector) { } // Handle the crash, get the name of the process from procfs. - if (!user_collector->HandleCrash(FLAGS_signal, FLAGS_pid, NULL)) { + if (!user_collector->HandleCrash(FLAGS_user, NULL)) { return 1; } return 0; diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 172b9c38c..192198345 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -17,6 +17,11 @@ # update_engine:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/update_engine/.) | tail -c 50000 +# The following rule is used for generating additional diagnostics when +# collection of user crashes fails. This output should not be too large +# as it is stored in memory. +crash_reporter-user-collection:echo "===ps output==="; ps auxw | tail -c 25000; echo "===dmesg output==="; dmesg | tail -c 25000; echo "===meminfo==="; cat /proc/meminfo + # The following rules are only for testing purposes. crash_log_test:echo hello world crash_log_recursion_test:sleep 1 && /usr/local/autotest/tests/crash_log_recursion_test diff --git a/crash_reporter/system_logging.cc b/crash_reporter/system_logging.cc index 578951418..04bb356ec 100644 --- a/crash_reporter/system_logging.cc +++ b/crash_reporter/system_logging.cc @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include +#include "crash-reporter/system_logging.h" + #include -#include "crash-reporter/system_logging.h" +#include "base/stringprintf.h" std::string SystemLoggingImpl::identity_; -SystemLoggingImpl::SystemLoggingImpl() { +SystemLoggingImpl::SystemLoggingImpl() : is_accumulating_(false) { } SystemLoggingImpl::~SystemLoggingImpl() { @@ -22,23 +23,33 @@ void SystemLoggingImpl::Initialize(const char *ident) { openlog(identity_.c_str(), LOG_PID, LOG_USER); } +void SystemLoggingImpl::LogWithLevel(int level, const char *format, + va_list arg_list) { + std::string message = StringPrintV(format, arg_list); + syslog(level, "%s", message.c_str()); + if (is_accumulating_) { + accumulator_.append(message); + accumulator_.push_back('\n'); + } +} + void SystemLoggingImpl::LogInfo(const char *format, ...) { va_list vl; va_start(vl, format); - vsyslog(LOG_INFO, format, vl); + LogWithLevel(LOG_INFO, format, vl); va_end(vl); } void SystemLoggingImpl::LogWarning(const char *format, ...) { va_list vl; va_start(vl, format); - vsyslog(LOG_WARNING, format, vl); + LogWithLevel(LOG_WARNING, format, vl); va_end(vl); } void SystemLoggingImpl::LogError(const char *format, ...) { va_list vl; va_start(vl, format); - vsyslog(LOG_ERR, format, vl); + LogWithLevel(LOG_ERR, format, vl); va_end(vl); } diff --git a/crash_reporter/system_logging.h b/crash_reporter/system_logging.h index 7ebfc5e59..d1f72a56e 100644 --- a/crash_reporter/system_logging.h +++ b/crash_reporter/system_logging.h @@ -5,6 +5,7 @@ #ifndef CRASH_REPORTER_SYSTEM_LOGGING_H_ #define CRASH_REPORTER_SYSTEM_LOGGING_H_ +#include #include class SystemLogging { @@ -13,8 +14,12 @@ class SystemLogging { virtual void LogInfo(const char *format, ...) = 0; virtual void LogWarning(const char *format, ...) = 0; virtual void LogError(const char *format, ...) = 0; + virtual void set_accumulating(bool value) = 0; + virtual std::string get_accumulator() = 0; }; +// SystemLoggingImpl implements SystemLogging but adds the +// capability of accumulating the log to a STL string. class SystemLoggingImpl : public SystemLogging { public: SystemLoggingImpl(); @@ -23,8 +28,18 @@ class SystemLoggingImpl : public SystemLogging { virtual void LogInfo(const char *format, ...); virtual void LogWarning(const char *format, ...); virtual void LogError(const char *format, ...); + virtual void set_accumulating(bool value) { + is_accumulating_ = value; + } + virtual std::string get_accumulator() { + return accumulator_; + } private: static std::string identity_; + std::string accumulator_; + bool is_accumulating_; + void LogWithLevel(int level, const char *format, + va_list arg_list); }; #endif // CRASH_REPORTER_SYSTEM_LOGGING_H_ diff --git a/crash_reporter/system_logging_mock.cc b/crash_reporter/system_logging_mock.cc index a2760e9d5..04ce853b7 100644 --- a/crash_reporter/system_logging_mock.cc +++ b/crash_reporter/system_logging_mock.cc @@ -33,3 +33,10 @@ void SystemLoggingMock::LogError(const char *format, ...) { log_ += "\n"; va_end(vl); } + +void SystemLoggingMock::set_accumulating(bool value) { +} + +std::string SystemLoggingMock::get_accumulator() { + return ""; +} diff --git a/crash_reporter/system_logging_mock.h b/crash_reporter/system_logging_mock.h index f4fb13343..56c6e5836 100644 --- a/crash_reporter/system_logging_mock.h +++ b/crash_reporter/system_logging_mock.h @@ -15,6 +15,8 @@ class SystemLoggingMock : public SystemLogging { virtual void LogInfo(const char *format, ...); virtual void LogWarning(const char *format, ...); virtual void LogError(const char *format, ...); + virtual void set_accumulating(bool value); + virtual std::string get_accumulator(); const std::string &log() { return log_; } diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 59b460bdc..a6dd091e7 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -5,6 +5,8 @@ #include "crash-reporter/user_collector.h" #include // For struct group. +#include +#include #include // For struct passwd. #include // For getpwuid_r, getgrnam_r, WEXITSTATUS. @@ -18,8 +20,8 @@ #include "gflags/gflags.h" #pragma GCC diagnostic ignored "-Wstrict-aliasing" -DEFINE_bool(core2md_failure_test, false, "Core2md failure test"); -DEFINE_bool(directory_failure_test, false, "Spool directory failure test"); +DEFINE_bool(core2md_failure, false, "Core2md failure test"); +DEFINE_bool(directory_failure, false, "Spool directory failure test"); DEFINE_string(filter_in, "", "Ignore all crashes but this for testing"); #pragma GCC diagnostic error "-Wstrict-aliasing" @@ -68,7 +70,11 @@ UserCollector::~UserCollector() { std::string UserCollector::GetPattern(bool enabled) const { if (enabled) { - return StringPrintf("|%s --signal=%%s --pid=%%p", our_path_.c_str()); + // Combine the three crash attributes into one parameter to try to reduce + // the size of the invocation line for crash_reporter since the kernel + // has a fixed-sized (128B) buffer that it will truncate into. Note that + // the kernel does not support quoted arguments in core_pattern. + return StringPrintf("|%s --user=%%p:%%s:%%e", our_path_.c_str()); } else { return "core"; } @@ -169,12 +175,6 @@ bool UserCollector::GetIdFromStatus(const char *prefix, return true; } -void UserCollector::LogCollectionError(const std::string &error_message) { - error_log_.append(error_message.c_str()); - error_log_.append("\n"); - logger_->LogError(error_message.c_str()); -} - void UserCollector::EnqueueCollectionErrorLog(pid_t pid, const std::string &exec) { FilePath crash_path; @@ -184,12 +184,24 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid, return; } std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); + std::string error_log = logger_->get_accumulator(); + FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog"); + if (GetLogContents(FilePath(kDefaultLogConfig), kCollectionErrorSignature, + diag_log_path)) { + // We load the contents of diag_log into memory and append it to + // the error log. We cannot just append to files because we need + // to always create new files to prevent attack. + std::string diag_log_contents; + file_util::ReadFileToString(diag_log_path, &diag_log_contents); + error_log.append(diag_log_contents); + file_util::Delete(diag_log_path, false); + } FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); // We must use WriteNewFile instead of file_util::WriteFile as we do // not want to write with root access to a symlink that an attacker // might have created. - WriteNewFile(log_path, error_log_.data(), error_log_.length()); + WriteNewFile(log_path, error_log.data(), error_log.length()); AddCrashMetaData("sig", kCollectionErrorSignature); WriteCrashMetaData(meta_path, exec, log_path.value()); } @@ -197,14 +209,13 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid, bool UserCollector::CopyOffProcFiles(pid_t pid, const FilePath &container_dir) { if (!file_util::CreateDirectory(container_dir)) { - LogCollectionError(StringPrintf("Could not create %s", - container_dir.value().c_str())); + logger_->LogError("Could not create %s", + container_dir.value().c_str()); return false; } FilePath process_path = GetProcessPath(pid); if (!file_util::PathExists(process_path)) { - LogCollectionError(StringPrintf("Path %s does not exist", - process_path.value().c_str())); + logger_->LogError("Path %s does not exist", process_path.value().c_str()); return false; } static const char *proc_files[] = { @@ -217,8 +228,7 @@ bool UserCollector::CopyOffProcFiles(pid_t pid, for (unsigned i = 0; i < arraysize(proc_files); ++i) { if (!file_util::CopyFile(process_path.Append(proc_files[i]), container_dir.Append(proc_files[i]))) { - LogCollectionError(StringPrintf("Could not copy %s file", - proc_files[i])); + logger_->LogError("Could not copy %s file", proc_files[i]); return false; } } @@ -230,24 +240,27 @@ bool UserCollector::GetCreatedCrashDirectory(pid_t pid, bool *out_of_capacity) { FilePath process_path = GetProcessPath(pid); std::string status; - if (FLAGS_directory_failure_test) { - LogCollectionError("Purposefully failing to create spool directory"); + if (FLAGS_directory_failure) { + logger_->LogError("Purposefully failing to create spool directory"); return false; } if (!file_util::ReadFileToString(process_path.Append("status"), &status)) { - LogCollectionError("Could not read status file"); + logger_->LogError("Could not read status file"); + logger_->LogInfo("Path %s FileExists: %d", + process_path.value().c_str(), + file_util::DirectoryExists(process_path)); return false; } int process_euid; if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { - LogCollectionError("Could not find euid in status file"); + logger_->LogError("Could not find euid in status file"); return false; } if (!GetCreatedCrashDirectoryByEuid(process_euid, crash_file_path, out_of_capacity)) { - LogCollectionError("Could not create crash directory"); + logger_->LogError("Could not create crash directory"); return false; } return true; @@ -260,7 +273,7 @@ bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { return true; } - LogCollectionError("Could not write core file"); + logger_->LogError("Could not write core file"); // If the file system was full, make sure we remove any remnants. file_util::Delete(core_path, false); return false; @@ -277,7 +290,7 @@ bool UserCollector::RunCoreToMinidump(const FilePath &core_path, core2md_arguments.push_back(procfs_directory.value().c_str()); core2md_arguments.push_back(minidump_path.value().c_str()); - if (FLAGS_core2md_failure_test) { + if (FLAGS_core2md_failure) { // To test how core2md errors are propagaged, cause an error // by forgetting a required argument. core2md_arguments.pop_back(); @@ -289,16 +302,16 @@ bool UserCollector::RunCoreToMinidump(const FilePath &core_path, std::string output; file_util::ReadFileToString(output_path, &output); if (errorlevel != 0) { - LogCollectionError(StringPrintf("Problem during %s [result=%d]: %s", - kCoreToMinidumpConverterPath, - errorlevel, - output.c_str())); + logger_->LogError("Problem during %s [result=%d]: %s", + kCoreToMinidumpConverterPath, + errorlevel, + output.c_str()); return false; } if (!file_util::PathExists(minidump_path)) { - LogCollectionError(StringPrintf("Minidump file %s was not created", - minidump_path.value().c_str())); + logger_->LogError("Minidump file %s was not created", + minidump_path.value().c_str()); return false; } return true; @@ -334,7 +347,7 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid, bool *out_of_capacity) { FilePath crash_path; if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) { - LogCollectionError("Unable to find/create process-specific crash path"); + logger_->LogError("Unable to find/create process-specific crash path"); return false; } @@ -342,6 +355,10 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid, // procfs entries and other temporary files used during conversion. FilePath container_dir = FilePath("/tmp").Append( StringPrintf("crash_reporter.%d", pid)); + // Delete a pre-existing directory from crash reporter that may have + // been left around for diagnostics from a failed conversion attempt. + // If we don't, existing files can cause forking to fail. + file_util::Delete(container_dir, true); std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); FilePath core_path = GetCrashPath(crash_path, dump_basename, "core"); FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); @@ -376,15 +393,34 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid, return true; } -bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { +bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes, + pid_t *pid, int *signal, + std::string *kernel_supplied_name) { + pcrecpp::RE re("(\\d+):(\\d+):(.*)"); + return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name); +} + +bool UserCollector::HandleCrash(const std::string &crash_attributes, + const char *force_exec) { CHECK(initialized_); + int pid = 0; + int signal = 0; + std::string kernel_supplied_name; + + if (!ParseCrashAttributes(crash_attributes, &pid, &signal, + &kernel_supplied_name)) { + logger_->LogError("Invalid parameter: --user=%s", crash_attributes.c_str()); + return false; + } + std::string exec; if (force_exec) { exec.assign(force_exec); } else if (!GetExecutableBaseNameFromPid(pid, &exec)) { - // If for some reason we don't have the base name, avoid completely - // failing by indicating an unknown name. - exec = "unknown"; + // If we cannot find the exec name, use the kernel supplied name. + // We don't always use the kernel's since it truncates the name to + // 16 characters. + exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str()); } // Allow us to test the crash reporting mechanism successfully even if @@ -394,7 +430,7 @@ bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { FLAGS_filter_in != exec)) { // We use a different format message to make it more obvious in tests // which crashes are test generated and which are real. - logger_->LogWarning("Ignoring crash from %s[%d] while filter_in=%s", + logger_->LogWarning("Ignoring crash from %s[%d] while filter_in=%s.", exec.c_str(), pid, FLAGS_filter_in.c_str()); return true; } @@ -421,7 +457,11 @@ bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { if (generate_diagnostics_) { bool out_of_capacity = false; - if (!ConvertAndEnqueueCrash(pid, exec, &out_of_capacity)) { + logger_->set_accumulating(true); + bool convert_and_enqueue_result = + ConvertAndEnqueueCrash(pid, exec, &out_of_capacity); + logger_->set_accumulating(false); + if (!convert_and_enqueue_result) { if (!out_of_capacity) EnqueueCollectionErrorLog(pid, exec); return false; diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 90bc578ee..851d587e6 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -40,7 +40,8 @@ class UserCollector : public CrashCollector { bool Disable() { return SetUpInternal(false); } // Handle a specific user crash. Returns true on success. - bool HandleCrash(int signal, int pid, const char *force_exec); + bool HandleCrash(const std::string &crash_attributes, + const char *force_exec); // Set (override the default) core file pattern. void set_core_pattern_file(const std::string &pattern) { @@ -61,6 +62,7 @@ class UserCollector : public CrashCollector { FRIEND_TEST(UserCollectorTest, GetProcessPath); FRIEND_TEST(UserCollectorTest, GetSymlinkTarget); FRIEND_TEST(UserCollectorTest, GetUserInfoFromName); + FRIEND_TEST(UserCollectorTest, ParseCrashAttributes); // Enumeration to pass to GetIdFromStatus. Must match the order // that the kernel lists IDs in the status file. @@ -109,6 +111,9 @@ class UserCollector : public CrashCollector { const FilePath &minidump_path); bool ConvertAndEnqueueCrash(int pid, const std::string &exec_name, bool *out_of_capacity); + bool ParseCrashAttributes(const std::string &crash_attributes, + pid_t *pid, int *signal, + std::string *kernel_supplied_name); bool generate_diagnostics_; std::string core_pattern_file_; @@ -116,9 +121,6 @@ class UserCollector : public CrashCollector { std::string our_path_; bool initialized_; - // String containing the current log of crash handling errors. - std::string error_log_; - static const char *kUserId; static const char *kGroupId; }; diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 942123fcb..53e227cf0 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -54,7 +54,7 @@ class UserCollectorTest : public ::testing::Test { TEST_F(UserCollectorTest, EnableOK) { ASSERT_TRUE(collector_.Enable()); - ExpectFileEquals("|/my/path --signal=%s --pid=%p", "test/core_pattern"); + ExpectFileEquals("|/my/path --user=%p:%s:%e", "test/core_pattern"); ExpectFileEquals("4", "test/core_pipe_limit"); ASSERT_EQ(s_crashes, 0); ASSERT_NE(logging_.log().find("Enabling user crash handling"), @@ -102,9 +102,36 @@ TEST_F(UserCollectorTest, DisableNoFileAccess) { std::string::npos); } +TEST_F(UserCollectorTest, ParseCrashAttributes) { + pid_t pid; + int signal; + std::string exec_name; + EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:foobar", + &pid, &signal, &exec_name)); + EXPECT_EQ(123456, pid); + EXPECT_EQ(11, signal); + EXPECT_EQ("foobar", exec_name); + + EXPECT_FALSE(collector_.ParseCrashAttributes("123456:11", + &pid, &signal, &exec_name)); + + EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:exec:extra", + &pid, &signal, &exec_name)); + EXPECT_EQ("exec:extra", exec_name); + + EXPECT_FALSE(collector_.ParseCrashAttributes("12345p:11:foobar", + &pid, &signal, &exec_name)); + + EXPECT_FALSE(collector_.ParseCrashAttributes("123456:1 :foobar", + &pid, &signal, &exec_name)); + + EXPECT_FALSE(collector_.ParseCrashAttributes("123456::foobar", + &pid, &signal, &exec_name)); +} + TEST_F(UserCollectorTest, HandleCrashWithoutMetrics) { s_metrics = false; - collector_.HandleCrash(10, 20, "foobar"); + collector_.HandleCrash("20:10:ignored", "foobar"); ASSERT_NE(std::string::npos, logging_.log().find( "Received crash notification for foobar[20] sig 10")); @@ -113,7 +140,7 @@ TEST_F(UserCollectorTest, HandleCrashWithoutMetrics) { TEST_F(UserCollectorTest, HandleNonChromeCrashWithMetrics) { s_metrics = true; - collector_.HandleCrash(2, 5, "chromeos-wm"); + collector_.HandleCrash("5:2:ignored", "chromeos-wm"); ASSERT_NE(std::string::npos, logging_.log().find( "Received crash notification for chromeos-wm[5] sig 2")); @@ -122,7 +149,7 @@ TEST_F(UserCollectorTest, HandleNonChromeCrashWithMetrics) { TEST_F(UserCollectorTest, HandleChromeCrashWithMetrics) { s_metrics = true; - collector_.HandleCrash(2, 5, "chrome"); + collector_.HandleCrash("5:2:ignored", "chrome"); ASSERT_NE(std::string::npos, logging_.log().find( "Received crash notification for chrome[5] sig 2")); From d49d362f61c7e702d557e563055b1c541ead5b67 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Wed, 9 Feb 2011 18:23:00 -0800 Subject: [PATCH 041/225] crash-reporter: when exe symlink read fails send diags and still ignore chrome crashes Change-Id: I0f43d5f61936ebb6420c357e58df45761facd4d0 BUG=6299 TEST=unit tests and UserCrash/CrashSender Review URL: http://codereview.chromium.org/6480009 --- crash_reporter/crash_reporter.cc | 7 +++-- crash_reporter/user_collector.cc | 28 ++++++++++++++++---- crash_reporter/user_collector.h | 1 + crash_reporter/user_collector_test.cc | 38 ++++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 8 deletions(-) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index ffe6cdfed..ab4c5ba3b 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -148,10 +148,13 @@ static int HandleUserCrash(UserCollector *user_collector) { return 0; } + // Accumulate logs to help in diagnosing failures during user collection. + s_system_log.set_accumulating(true); // Handle the crash, get the name of the process from procfs. - if (!user_collector->HandleCrash(FLAGS_user, NULL)) { + bool handled = user_collector->HandleCrash(FLAGS_user, NULL); + s_system_log.set_accumulating(false); + if (!handled) return 1; - } return 0; } diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index a6dd091e7..6e6c76596 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -114,6 +114,9 @@ bool UserCollector::GetSymlinkTarget(const FilePath &symlink, buffer.reset(new char[max_size + 1]); ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size); if (size < 0) { + int saved_errno = errno; + logger_->LogError("Readlink failed on %s with %d", + symlink.value().c_str(), saved_errno); return false; } buffer[size] = 0; @@ -136,8 +139,25 @@ bool UserCollector::GetSymlinkTarget(const FilePath &symlink, bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid, std::string *base_name) { FilePath target; - if (!GetSymlinkTarget(GetProcessPath(pid).Append("exe"), &target)) + FilePath process_path = GetProcessPath(pid); + FilePath exe_path = process_path.Append("exe"); + if (!GetSymlinkTarget(exe_path, &target)) { + logger_->LogInfo("GetSymlinkTarget failed - Path %s DirectoryExists: %d", + process_path.value().c_str(), + file_util::DirectoryExists(process_path)); + // Try to further diagnose exe readlink failure cause. + struct stat buf; + int stat_result = stat(exe_path.value().c_str(), &buf); + int saved_errno = errno; + if (stat_result < 0) { + logger_->LogInfo("stat %s failed: %d %d", exe_path.value().c_str(), + stat_result, saved_errno); + } else { + logger_->LogInfo("stat %s succeeded: st_mode=%d", + exe_path.value().c_str(), buf.st_mode); + } return false; + } *base_name = target.BaseName().value(); return true; } @@ -247,7 +267,7 @@ bool UserCollector::GetCreatedCrashDirectory(pid_t pid, if (!file_util::ReadFileToString(process_path.Append("status"), &status)) { logger_->LogError("Could not read status file"); - logger_->LogInfo("Path %s FileExists: %d", + logger_->LogInfo("Path %s DirectoryExists: %d", process_path.value().c_str(), file_util::DirectoryExists(process_path)); return false; @@ -444,7 +464,7 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, // Treat Chrome crashes as if the user opted-out. We stop counting Chrome // crashes towards user crashes, so user crashes really mean non-Chrome // user-space crashes. - if (exec == "chrome") { + if (exec == "chrome" || exec == "supplied_chrome") { feedback = false; handling_string = "ignoring - chrome crash"; } @@ -457,10 +477,8 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, if (generate_diagnostics_) { bool out_of_capacity = false; - logger_->set_accumulating(true); bool convert_and_enqueue_result = ConvertAndEnqueueCrash(pid, exec, &out_of_capacity); - logger_->set_accumulating(false); if (!convert_and_enqueue_result) { if (!out_of_capacity) EnqueueCollectionErrorLog(pid, exec); diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 851d587e6..21ef14b53 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -58,6 +58,7 @@ class UserCollector : public CrashCollector { FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPath); FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPid); FRIEND_TEST(UserCollectorTest, CopyOffProcFilesOK); + FRIEND_TEST(UserCollectorTest, GetExecutableBaseNameFromPid); FRIEND_TEST(UserCollectorTest, GetIdFromStatus); FRIEND_TEST(UserCollectorTest, GetProcessPath); FRIEND_TEST(UserCollectorTest, GetSymlinkTarget); diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 53e227cf0..9a1d687ef 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -158,6 +158,17 @@ TEST_F(UserCollectorTest, HandleChromeCrashWithMetrics) { ASSERT_EQ(s_crashes, 0); } +TEST_F(UserCollectorTest, HandleSuppliedChromeCrashWithMetrics) { + s_metrics = true; + collector_.HandleCrash("0:2:chrome", NULL); + ASSERT_NE(std::string::npos, + logging_.log().find( + "Received crash notification for supplied_chrome[0] sig 2")); + ASSERT_NE(std::string::npos, + logging_.log().find("(ignoring - chrome crash)")); + ASSERT_EQ(s_crashes, 0); +} + TEST_F(UserCollectorTest, GetProcessPath) { FilePath path = collector_.GetProcessPath(100); ASSERT_EQ("/proc/100", path.value()); @@ -167,7 +178,9 @@ TEST_F(UserCollectorTest, GetSymlinkTarget) { FilePath result; ASSERT_FALSE(collector_.GetSymlinkTarget(FilePath("/does_not_exist"), &result)); - + ASSERT_NE(std::string::npos, + logging_.log().find( + "Readlink failed on /does_not_exist with 2")); std::string long_link; for (int i = 0; i < 50; ++i) long_link += "0123456789"; @@ -185,6 +198,29 @@ TEST_F(UserCollectorTest, GetSymlinkTarget) { } } +TEST_F(UserCollectorTest, GetExecutableBaseNameFromPid) { + std::string base_name; + EXPECT_FALSE(collector_.GetExecutableBaseNameFromPid(0, &base_name)); + EXPECT_NE(std::string::npos, + logging_.log().find( + "Readlink failed on /proc/0/exe with 2")); + EXPECT_NE(std::string::npos, + logging_.log().find( + "GetSymlinkTarget failed - Path " + "/proc/0 DirectoryExists: 0")); + EXPECT_NE(std::string::npos, + logging_.log().find( + "stat /proc/0/exe failed: -1 2")); + + logging_.clear(); + pid_t my_pid = getpid(); + EXPECT_TRUE(collector_.GetExecutableBaseNameFromPid(my_pid, &base_name)); + EXPECT_EQ(std::string::npos, + logging_.log().find( + "Readlink failed")); + EXPECT_EQ("user_collector_test", base_name); +} + TEST_F(UserCollectorTest, GetIdFromStatus) { int id = 1; EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId, From 604f5c0d3dfdb60302d68f1c08c86728f82aae46 Mon Sep 17 00:00:00 2001 From: Thieu Le Date: Mon, 28 Feb 2011 10:51:18 -0800 Subject: [PATCH 042/225] Modify crash-reporter to collect crash information for unofficial builds. BUG=chromium-os:12207 TESTS=logging_UserCrash, logging_CrashSender, manual test which involves purposely crashing a test to determine if minidump information is properly collected Change-Id: Id2195125289a695515b1722442116d6a6d9c4895 Review URL: http://codereview.chromium.org/6559003 --- crash_reporter/crash_collector.cc | 5 +++++ crash_reporter/crash_collector.h | 3 +++ crash_reporter/user_collector.cc | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index afd72d1e1..bdad63f55 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -24,6 +24,7 @@ static const char kLsbRelease[] = "/etc/lsb-release"; static const char kShellPath[] = "/bin/sh"; static const char kSystemCrashPath[] = "/var/spool/crash"; static const char kUserCrashPath[] = "/home/chronos/user/crash"; +static const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress"; // Directory mode of the user crash spool directory. static const mode_t kUserCrashPathMode = 0755; @@ -410,3 +411,7 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, logger_->LogError("Unable to write %s", meta_path.value().c_str()); } } + +bool CrashCollector::IsCrashTestInProgress() { + return file_util::PathExists(FilePath(kCrashTestInProgressPath)); +} diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index f3fcbe596..668039991 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -138,6 +138,9 @@ class CrashCollector { const std::string &exec_name, const std::string &payload_path); + // Returns true if the a crash test is currently running. + bool IsCrashTestInProgress(); + CountCrashFunction count_crash_function_; IsFeedbackAllowedFunction is_feedback_allowed_function_; SystemLogging *logger_; diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 6e6c76596..13df5ce91 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -472,6 +472,13 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", exec.c_str(), pid, signal, handling_string); + // For developer builds, we always want to keep the crash reports unless + // we're testing the crash facilities themselves. + if (file_util::PathExists(FilePath(kLeaveCoreFile)) && + !IsCrashTestInProgress()) { + feedback = true; + } + if (feedback) { count_crash_function_(); From f92f4caa8471f00f4004f438e51e1c8cc8ac5358 Mon Sep 17 00:00:00 2001 From: Thieu Le Date: Mon, 28 Feb 2011 12:02:59 -0800 Subject: [PATCH 043/225] Revert "Modify crash-reporter to collect crash information for unofficial builds." This reverts commit cb5a147e46dbfa454df9d56b4da23480742597c8. BUG=None TEST= Review URL: http://codereview.chromium.org/6596045 Change-Id: If07530a743b19a76e9a15ef08825c45c38f9dd2b --- crash_reporter/crash_collector.cc | 5 ----- crash_reporter/crash_collector.h | 3 --- crash_reporter/user_collector.cc | 7 ------- 3 files changed, 15 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index bdad63f55..afd72d1e1 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -24,7 +24,6 @@ static const char kLsbRelease[] = "/etc/lsb-release"; static const char kShellPath[] = "/bin/sh"; static const char kSystemCrashPath[] = "/var/spool/crash"; static const char kUserCrashPath[] = "/home/chronos/user/crash"; -static const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress"; // Directory mode of the user crash spool directory. static const mode_t kUserCrashPathMode = 0755; @@ -411,7 +410,3 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, logger_->LogError("Unable to write %s", meta_path.value().c_str()); } } - -bool CrashCollector::IsCrashTestInProgress() { - return file_util::PathExists(FilePath(kCrashTestInProgressPath)); -} diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 668039991..f3fcbe596 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -138,9 +138,6 @@ class CrashCollector { const std::string &exec_name, const std::string &payload_path); - // Returns true if the a crash test is currently running. - bool IsCrashTestInProgress(); - CountCrashFunction count_crash_function_; IsFeedbackAllowedFunction is_feedback_allowed_function_; SystemLogging *logger_; diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 13df5ce91..6e6c76596 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -472,13 +472,6 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", exec.c_str(), pid, signal, handling_string); - // For developer builds, we always want to keep the crash reports unless - // we're testing the crash facilities themselves. - if (file_util::PathExists(FilePath(kLeaveCoreFile)) && - !IsCrashTestInProgress()) { - feedback = true; - } - if (feedback) { count_crash_function_(); From a324932e066ccb7ed9d62db9c409ee2c10052554 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Thu, 3 Mar 2011 08:47:38 -0800 Subject: [PATCH 044/225] crash-reporter: Use standard logging and new libchromeos Process code Change-Id: Ie085d61d4d79c0df38e51debaa044d701a860c89 BUG=none TEST=unit tests / UserCrash / CrashSender Review URL: http://codereview.chromium.org/6517001 --- crash_reporter/Makefile | 5 +- crash_reporter/crash_collector.cc | 105 ++++------------- crash_reporter/crash_collector.h | 12 +- crash_reporter/crash_collector_test.cc | 106 ++--------------- crash_reporter/crash_reporter.cc | 47 +++++--- crash_reporter/kernel_collector.cc | 26 ++-- crash_reporter/kernel_collector_test.cc | 40 +++---- crash_reporter/system_logging.cc | 55 --------- crash_reporter/system_logging.h | 45 ------- crash_reporter/system_logging_mock.cc | 42 ------- crash_reporter/system_logging_mock.h | 31 ----- crash_reporter/test_helpers.h | 18 --- crash_reporter/unclean_shutdown_collector.cc | 21 ++-- .../unclean_shutdown_collector_test.cc | 26 ++-- crash_reporter/user_collector.cc | 111 +++++++++--------- crash_reporter/user_collector.h | 1 - crash_reporter/user_collector_test.cc | 98 ++++++---------- 17 files changed, 210 insertions(+), 579 deletions(-) delete mode 100644 crash_reporter/system_logging.cc delete mode 100644 crash_reporter/system_logging.h delete mode 100644 crash_reporter/system_logging_mock.cc delete mode 100644 crash_reporter/system_logging_mock.h delete mode 100644 crash_reporter/test_helpers.h diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index 0523c199e..b649bea24 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -7,10 +7,9 @@ REPORTER_BINS = $(CRASH_REPORTER) CRASH_OBJS = \ crash_collector.o \ kernel_collector.o \ - system_logging.o \ unclean_shutdown_collector.o \ user_collector.o -TEST_OBJS = $(CRASH_OBJS) system_logging_mock.o +TEST_OBJS = $(CRASH_OBJS) TEST_BINS = \ crash_collector_test \ kernel_collector_test \ @@ -20,7 +19,7 @@ TEST_BINS = \ LDCONFIG = $(shell $(PKG_CONFIG) --libs libpcrecpp) # -lglib-2.0 is needed by libbase.a now. -COMMON_LIBS = -lbase -lpthread -lglib-2.0 -lgflags -lrt $(LDCONFIG) +COMMON_LIBS = -lchromeos -lbase -lpthread -lglib-2.0 -lgflags -lrt $(LDCONFIG) REPORTER_LIBS = $(COMMON_LIBS) -lmetrics TEST_LIBS = $(COMMON_LIBS) -lgtest -lgmock diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index afd72d1e1..9600b44a5 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -17,7 +17,7 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" -#include "crash-reporter/system_logging.h" +#include "chromeos/process.h" static const char kDefaultUserName[] = "chronos"; static const char kLsbRelease[] = "/etc/lsb-release"; @@ -54,15 +54,12 @@ CrashCollector::~CrashCollector() { void CrashCollector::Initialize( CrashCollector::CountCrashFunction count_crash_function, - CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function, - SystemLogging *logger) { + CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function) { CHECK(count_crash_function != NULL); CHECK(is_feedback_allowed_function != NULL); - CHECK(logger != NULL); count_crash_function_ = count_crash_function; is_feedback_allowed_function_ = is_feedback_allowed_function; - logger_ = logger; } int CrashCollector::WriteNewFile(const FilePath &filename, @@ -79,63 +76,6 @@ int CrashCollector::WriteNewFile(const FilePath &filename, return rv; } -int CrashCollector::ForkExecAndPipe(std::vector &arguments, - const char *output_file) { - // Copy off a writeable version of arguments. - scoped_array argv(new char *[arguments.size() + 1]); - int total_args_size = 0; - for (size_t i = 0; i < arguments.size(); ++i) { - if (arguments[i] == NULL) { - logger_->LogError("Bad parameter"); - return -1; - } - total_args_size += strlen(arguments[i]) + 1; - } - scoped_array buffer(new char[total_args_size]); - char *buffer_pointer = &buffer[0]; - - for (size_t i = 0; i < arguments.size(); ++i) { - argv[i] = buffer_pointer; - strcpy(buffer_pointer, arguments[i]); - buffer_pointer += strlen(arguments[i]); - *buffer_pointer = '\0'; - ++buffer_pointer; - } - argv[arguments.size()] = NULL; - - int pid = fork(); - if (pid < 0) { - logger_->LogError("Fork failed: %d", errno); - return -1; - } - - if (pid == 0) { - int output_handle = HANDLE_EINTR( - open(output_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666)); - if (output_handle < 0) { - logger_->LogError("Could not create %s: %d", output_file, errno); - // Avoid exit() to avoid atexit handlers from parent. - _exit(127); - } - dup2(output_handle, 1); - dup2(output_handle, 2); - execv(argv[0], &argv[0]); - logger_->LogError("Exec failed: %d", errno); - _exit(127); - } - - int status = 0; - if (HANDLE_EINTR(waitpid(pid, &status, 0)) < 0) { - logger_->LogError("Problem waiting for pid: %d", errno); - return -1; - } - if (!WIFEXITED(status)) { - logger_->LogError("Process did not exit normally: %d", status); - return -1; - } - return WEXITSTATUS(status); -} - std::string CrashCollector::Sanitize(const std::string &name) { std::string result = name; for (size_t i = 0; i < name.size(); ++i) { @@ -199,7 +139,7 @@ bool CrashCollector::GetUserInfoFromName(const std::string &name, if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage), &passwd_result) != 0 || passwd_result == NULL) { - logger_->LogError("Cannot find user named %s", name.c_str()); + LOG(ERROR) << "Cannot find user named " << name; return false; } @@ -225,7 +165,7 @@ bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid, if (!GetUserInfoFromName(kDefaultUserName, &default_user_id, &default_user_group)) { - logger_->LogError("Could not find default user info"); + LOG(ERROR) << "Could not find default user info"; return false; } mode_t directory_mode; @@ -247,15 +187,15 @@ bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid, chown(crash_directory->value().c_str(), directory_owner, directory_group) < 0) { - logger_->LogError("Unable to create appropriate crash directory"); + LOG(ERROR) << "Unable to create appropriate crash directory"; return false; } umask(old_mask); } if (!file_util::PathExists(*crash_directory)) { - logger_->LogError("Unable to create crash directory %s", - crash_directory->value().c_str()); + LOG(ERROR) << "Unable to create crash directory " + << crash_directory->value().c_str(); return false; } @@ -297,10 +237,9 @@ bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) { basenames.insert(basename); if (basenames.size() >= static_cast(kMaxCrashDirectorySize)) { - logger_->LogWarning( - "Crash directory %s already full with %d pending reports", - crash_directory.value().c_str(), - kMaxCrashDirectorySize); + LOG(WARNING) << "Crash directory " << crash_directory.value() + << " already full with " << kMaxCrashDirectorySize + << " pending reports"; full = true; break; } @@ -350,24 +289,24 @@ bool CrashCollector::GetLogContents(const FilePath &config_path, const FilePath &output_file) { std::map log_commands; if (!ReadKeyValueFile(config_path, ':', &log_commands)) { - logger_->LogInfo("Unable to read log configuration file %s", - config_path.value().c_str()); + LOG(INFO) << "Unable to read log configuration file " + << config_path.value(); return false; } if (log_commands.find(exec_name) == log_commands.end()) return false; - std::vector command; - command.push_back(kShellPath); - command.push_back("-c"); + chromeos::ProcessImpl diag_process; + diag_process.AddArg(kShellPath); std::string shell_command = log_commands[exec_name]; - command.push_back(shell_command.c_str()); + diag_process.AddStringOption("-c", shell_command); + diag_process.RedirectOutput(output_file.value()); - int fork_result = ForkExecAndPipe(command, output_file.value().c_str()); - if (fork_result != 0) { - logger_->LogInfo("Running shell command %s failed with: %d", - shell_command.c_str(), fork_result); + int result = diag_process.Run(); + if (result != 0) { + LOG(INFO) << "Running shell command " << shell_command << "failed with: " + << result; return false; } return true; @@ -383,7 +322,7 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, const std::string &payload_path) { std::map contents; if (!ReadKeyValueFile(FilePath(std::string(lsb_release_)), '=', &contents)) { - logger_->LogError("Problem parsing %s", lsb_release_); + LOG(ERROR) << "Problem parsing " << lsb_release_; // Even though there was some failure, take as much as we could read. } std::string version("unknown"); @@ -407,6 +346,6 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, // do not want to write with root access to a symlink that an attacker // might have created. if (WriteNewFile(meta_path, meta_data.c_str(), meta_data.size()) < 0) { - logger_->LogError("Unable to write %s", meta_path.value().c_str()); + LOG(ERROR) << "Unable to write " << meta_path.value(); } } diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index f3fcbe596..3c09e2fdf 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -13,8 +13,6 @@ #include "base/file_path.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST -class SystemLogging; - // User crash collector. class CrashCollector { public: @@ -26,11 +24,9 @@ class CrashCollector { virtual ~CrashCollector(); // Initialize the crash collector for detection of crashes, given a - // crash counting function, metrics collection enabled oracle, and - // system logger facility. + // crash counting function, and metrics collection enabled oracle. void Initialize(CountCrashFunction count_crash, - IsFeedbackAllowedFunction is_metrics_allowed, - SystemLogging *logger); + IsFeedbackAllowedFunction is_metrics_allowed); protected: friend class CrashCollectorTest; @@ -66,9 +62,6 @@ class CrashCollector { // Otherwise returns the number of bytes written. int WriteNewFile(const FilePath &filename, const char *data, int size); - int ForkExecAndPipe(std::vector &arguments, - const char *output_file); - // Return a filename that has only [a-z0-1_] characters by mapping // all others into '_'. std::string Sanitize(const std::string &name); @@ -140,7 +133,6 @@ class CrashCollector { CountCrashFunction count_crash_function_; IsFeedbackAllowedFunction is_feedback_allowed_function_; - SystemLogging *logger_; std::string extra_metadata_; const char *forced_crash_directory_; const char *lsb_release_; diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 8100a5f54..d98e1af97 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -6,9 +6,9 @@ #include "base/file_util.h" #include "base/string_util.h" +#include "chromeos/syslog_logging.h" +#include "chromeos/test_helpers.h" #include "crash-reporter/crash_collector.h" -#include "crash-reporter/system_logging_mock.h" -#include "crash-reporter/test_helpers.h" #include "gflags/gflags.h" #include "gtest/gtest.h" @@ -18,6 +18,8 @@ static const char kBinCp[] = "/bin/cp"; static const char kBinEcho[] = "/bin/echo"; static const char kBinFalse[] = "/bin/false"; +using chromeos::FindLog; + void CountCrash() { ADD_FAILURE(); } @@ -31,10 +33,10 @@ class CrashCollectorTest : public ::testing::Test { public: void SetUp() { collector_.Initialize(CountCrash, - IsMetrics, - &logging_); + IsMetrics); test_dir_ = FilePath("test"); file_util::CreateDirectory(test_dir_); + chromeos::ClearLog(); } void TearDown() { @@ -44,7 +46,6 @@ class CrashCollectorTest : public ::testing::Test { bool CheckHasCapacity(); protected: - SystemLoggingMock logging_; CrashCollector collector_; FilePath test_dir_; }; @@ -52,7 +53,6 @@ class CrashCollectorTest : public ::testing::Test { TEST_F(CrashCollectorTest, Initialize) { ASSERT_TRUE(CountCrash == collector_.count_crash_function_); ASSERT_TRUE(IsMetrics == collector_.is_feedback_allowed_function_); - ASSERT_TRUE(&logging_ == collector_.logger_); } TEST_F(CrashCollectorTest, WriteNewFile) { @@ -154,7 +154,7 @@ TEST_F(CrashCollectorTest, GetCrashPath) { bool CrashCollectorTest::CheckHasCapacity() { static const char kFullMessage[] = "Crash directory test already full"; bool has_capacity = collector_.CheckHasCapacity(test_dir_); - bool has_message = (logging_.log().find(kFullMessage) != std::string::npos); + bool has_message = FindLog(kFullMessage); EXPECT_EQ(has_message, !has_capacity); return has_capacity; } @@ -298,24 +298,24 @@ TEST_F(CrashCollectorTest, MetaData) { symlink(kMetaFileBasename, meta_symlink_path.value().c_str())); ASSERT_TRUE(file_util::PathExists(meta_symlink_path)); - logging_.clear(); + chromeos::ClearLog(); collector_.WriteCrashMetaData(meta_symlink_path, "kernel", payload_file.value()); - // Target metadata contents sould have stayed the same. + // Target metadata contents should have stayed the same. contents.clear(); EXPECT_TRUE(file_util::ReadFileToString(meta_file, &contents)); EXPECT_EQ(kExpectedMeta, contents); - EXPECT_NE(std::string::npos, logging_.log().find("Unable to write")); + EXPECT_TRUE(FindLog("Unable to write")); // Test target of dangling symlink is not created. file_util::Delete(meta_file, false); ASSERT_FALSE(file_util::PathExists(meta_file)); - logging_.clear(); + chromeos::ClearLog(); collector_.WriteCrashMetaData(meta_symlink_path, "kernel", payload_file.value()); EXPECT_FALSE(file_util::PathExists(meta_file)); - EXPECT_NE(std::string::npos, logging_.log().find("Unable to write")); + EXPECT_TRUE(FindLog("Unable to write")); } TEST_F(CrashCollectorTest, GetLogContents) { @@ -341,87 +341,7 @@ TEST_F(CrashCollectorTest, GetLogContents) { EXPECT_EQ("hello world\n", contents); } -class ForkExecAndPipeTest : public CrashCollectorTest { - public: - void SetUp() { - CrashCollectorTest::SetUp(); - output_file_ = "test/fork_out"; - file_util::Delete(FilePath(output_file_), false); - } - - void TearDown() { - CrashCollectorTest::TearDown(); - } - - protected: - std::vector args_; - const char *output_file_; -}; - -TEST_F(ForkExecAndPipeTest, Basic) { - args_.push_back(kBinEcho); - args_.push_back("hello world"); - EXPECT_EQ(0, collector_.ForkExecAndPipe(args_, output_file_)); - ExpectFileEquals("hello world\n", output_file_); - EXPECT_EQ("", logging_.log()); -} - -TEST_F(ForkExecAndPipeTest, NonZeroReturnValue) { - args_.push_back(kBinFalse); - EXPECT_EQ(1, collector_.ForkExecAndPipe(args_, output_file_)); - ExpectFileEquals("", output_file_); - EXPECT_EQ("", logging_.log()); -} - -TEST_F(ForkExecAndPipeTest, BadOutputFile) { - EXPECT_EQ(127, collector_.ForkExecAndPipe(args_, "/bad/path")); -} - -TEST_F(ForkExecAndPipeTest, ExistingOutputFile) { - args_.push_back(kBinEcho); - args_.push_back("hello world"); - EXPECT_FALSE(file_util::PathExists(FilePath(output_file_))); - EXPECT_EQ(0, collector_.ForkExecAndPipe(args_, output_file_)); - EXPECT_TRUE(file_util::PathExists(FilePath(output_file_))); - EXPECT_EQ(127, collector_.ForkExecAndPipe(args_, output_file_)); -} - -TEST_F(ForkExecAndPipeTest, BadExecutable) { - args_.push_back("false"); - EXPECT_EQ(127, collector_.ForkExecAndPipe(args_, output_file_)); -} - -TEST_F(ForkExecAndPipeTest, StderrCaptured) { - std::string contents; - args_.push_back(kBinCp); - EXPECT_EQ(1, collector_.ForkExecAndPipe(args_, output_file_)); - EXPECT_TRUE(file_util::ReadFileToString(FilePath(output_file_), - &contents)); - EXPECT_NE(std::string::npos, contents.find("missing file operand")); - EXPECT_EQ("", logging_.log()); -} - -TEST_F(ForkExecAndPipeTest, NULLParam) { - args_.push_back(NULL); - EXPECT_EQ(-1, collector_.ForkExecAndPipe(args_, output_file_)); - EXPECT_NE(std::string::npos, - logging_.log().find("Bad parameter")); -} - -TEST_F(ForkExecAndPipeTest, NoParams) { - EXPECT_EQ(127, collector_.ForkExecAndPipe(args_, output_file_)); -} - -TEST_F(ForkExecAndPipeTest, SegFaultHandling) { - args_.push_back(kBinBash); - args_.push_back("-c"); - args_.push_back("kill -SEGV $$"); - EXPECT_EQ(-1, collector_.ForkExecAndPipe(args_, output_file_)); - EXPECT_NE(std::string::npos, - logging_.log().find("Process did not exit normally")); -} - int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); + SetUpTests(&argc, argv, false); return RUN_ALL_TESTS(); } diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index ab4c5ba3b..42998e953 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -2,14 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include // for open + #include #include "base/file_util.h" #include "base/command_line.h" #include "base/logging.h" #include "base/string_util.h" +#include "chromeos/syslog_logging.h" #include "crash-reporter/kernel_collector.h" -#include "crash-reporter/system_logging.h" #include "crash-reporter/unclean_shutdown_collector.h" #include "crash-reporter/user_collector.h" #include "gflags/gflags.h" @@ -41,7 +43,6 @@ enum CrashKinds { }; static MetricsLibrary s_metrics_lib; -static SystemLoggingImpl s_system_log; static bool IsFeedbackAllowed() { return s_metrics_lib.AreMetricsEnabled(); @@ -96,9 +97,7 @@ static void CountUserCrash() { // to be restarted anyway. int status = system(command.c_str()); - if (status != 0) { - s_system_log.LogWarning("dbus-send running failed"); - } + LOG_IF(WARNING, status != 0) << "dbus-send running failed"; } static int Initialize(KernelCollector *kernel_collector, @@ -149,10 +148,10 @@ static int HandleUserCrash(UserCollector *user_collector) { } // Accumulate logs to help in diagnosing failures during user collection. - s_system_log.set_accumulating(true); + chromeos::LogToString(true); // Handle the crash, get the name of the process from procfs. bool handled = user_collector->HandleCrash(FLAGS_user, NULL); - s_system_log.set_accumulating(false); + chromeos::LogToString(false); if (!handled) return 1; return 0; @@ -178,31 +177,45 @@ static int GenerateKernelSignature(KernelCollector *kernel_collector) { return 0; } +// Ensure stdout, stdin, and stderr are open file descriptors. If +// they are not, any code which writes to stderr/stdout may write out +// to files opened during execution. In particular, when +// crash_reporter is run by the kernel coredump pipe handler (via +// kthread_create/kernel_execve), it will not have file table entries +// 1 and 2 (stdout and stderr) populated. We populate them here. +static void OpenStandardFileDescriptors() { + int new_fd = -1; + // We open /dev/null to fill in any of the standard [0, 2] file + // descriptors. We leave these open for the duration of the + // process. This works because open returns the lowest numbered + // invalid fd. + do { + new_fd = open("/dev/null", 0); + CHECK(new_fd >= 0) << "Unable to open /dev/null"; + } while (new_fd >= 0 && new_fd <= 2); + close(new_fd); +} + int main(int argc, char *argv[]) { + OpenStandardFileDescriptors(); google::ParseCommandLineFlags(&argc, &argv, true); FilePath my_path(argv[0]); file_util::AbsolutePath(&my_path); s_metrics_lib.Init(); CommandLine::Init(argc, argv); - logging::InitLogging(NULL, - logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG, - logging::DONT_LOCK_LOG_FILE, - logging::DELETE_OLD_LOG_FILE); - s_system_log.Initialize(my_path.BaseName().value().c_str()); + chromeos::OpenLog(my_path.BaseName().value().c_str(), true); + chromeos::InitLog(chromeos::kLogToSyslog); KernelCollector kernel_collector; kernel_collector.Initialize(CountKernelCrash, - IsFeedbackAllowed, - &s_system_log); + IsFeedbackAllowed); UserCollector user_collector; user_collector.Initialize(CountUserCrash, my_path.value(), IsFeedbackAllowed, - &s_system_log, true); // generate_diagnostics UncleanShutdownCollector unclean_shutdown_collector; unclean_shutdown_collector.Initialize(CountUncleanShutdown, - IsFeedbackAllowed, - &s_system_log); + IsFeedbackAllowed); if (FLAGS_init) { return Initialize(&kernel_collector, diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 4fa51e26e..652e7ff3a 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -7,7 +7,6 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" -#include "crash-reporter/system_logging.h" const char KernelCollector::kClearingSequence[] = " "; static const char kDefaultKernelStackSignature[] = @@ -41,8 +40,7 @@ bool KernelCollector::LoadPreservedDump(std::string *contents) { // clear contents since ReadFileToString actually appends to the string. contents->clear(); if (!file_util::ReadFileToString(preserved_dump_path_, contents)) { - logger_->LogError("Unable to read %s", - preserved_dump_path_.value().c_str()); + LOG(ERROR) << "Unable to read " << preserved_dump_path_.value(); return false; } return true; @@ -50,13 +48,13 @@ bool KernelCollector::LoadPreservedDump(std::string *contents) { bool KernelCollector::Enable() { if (!file_util::PathExists(preserved_dump_path_)) { - logger_->LogWarning("Kernel does not support crash dumping"); + LOG(WARNING) << "Kernel does not support crash dumping"; return false; } // To enable crashes, we will eventually need to set // the chnv bit in BIOS, but it does not yet work. - logger_->LogInfo("Enabling kernel crash handling"); + LOG(INFO) << "Enabling kernel crash handling"; is_enabled_ = true; return true; } @@ -68,10 +66,10 @@ bool KernelCollector::ClearPreservedDump() { preserved_dump_path_, kClearingSequence, strlen(kClearingSequence)) != strlen(kClearingSequence)) { - logger_->LogError("Failed to clear kernel crash dump"); + LOG(ERROR) << "Failed to clear kernel crash dump"; return false; } - logger_->LogInfo("Cleared kernel crash diagnostics"); + LOG(INFO) << "Cleared kernel crash diagnostics"; return true; } @@ -256,10 +254,9 @@ bool KernelCollector::Collect() { bool feedback = is_feedback_allowed_function_(); - logger_->LogInfo("Received prior crash notification from " - "kernel (signature %s) (%s)", - signature.c_str(), - feedback ? "handling" : "ignoring - no consent"); + LOG(INFO) << "Received prior crash notification from " + << "kernel (signature " << signature << ") (" + << (feedback ? "handling" : "ignoring - no consent") << ")"; if (feedback) { count_crash_function_(); @@ -284,8 +281,8 @@ bool KernelCollector::Collect() { kernel_dump.data(), kernel_dump.length()) != static_cast(kernel_dump.length())) { - logger_->LogInfo("Failed to write kernel dump to %s", - kernel_crash_path.value().c_str()); + LOG(INFO) << "Failed to write kernel dump to " + << kernel_crash_path.value().c_str(); return true; } @@ -296,8 +293,7 @@ bool KernelCollector::Collect() { kKernelExecName, kernel_crash_path.value()); - logger_->LogInfo("Stored kcrash to %s", - kernel_crash_path.value().c_str()); + LOG(INFO) << "Stored kcrash to " << kernel_crash_path.value(); } if (!ClearPreservedDump()) { return false; diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 7b84229aa..f7c88f09a 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -6,8 +6,9 @@ #include "base/file_util.h" #include "base/string_util.h" +#include "chromeos/syslog_logging.h" +#include "chromeos/test_helpers.h" #include "crash-reporter/kernel_collector.h" -#include "crash-reporter/system_logging_mock.h" #include "gflags/gflags.h" #include "gtest/gtest.h" @@ -17,6 +18,8 @@ static bool s_metrics = false; static const char kTestKCrash[] = "test/kcrash"; static const char kTestCrashDirectory[] = "test/crash_directory"; +using chromeos::FindLog; + void CountCrash() { ++s_crashes; } @@ -30,13 +33,13 @@ class KernelCollectorTest : public ::testing::Test { s_crashes = 0; s_metrics = true; collector_.Initialize(CountCrash, - IsMetrics, - &logging_); + IsMetrics); mkdir("test", 0777); test_kcrash_ = FilePath(kTestKCrash); collector_.OverridePreservedDumpPath(test_kcrash_); unlink(kTestKCrash); mkdir(kTestCrashDirectory, 0777); + chromeos::ClearLog(); } protected: void WriteStringToFile(const FilePath &file_path, @@ -48,7 +51,6 @@ class KernelCollectorTest : public ::testing::Test { void SetUpSuccessfulCollect(); void CheckPreservedDumpClear(); - SystemLoggingMock logging_; KernelCollector collector_; FilePath test_kcrash_; }; @@ -68,10 +70,8 @@ TEST_F(KernelCollectorTest, LoadPreservedDump) { TEST_F(KernelCollectorTest, EnableMissingKernel) { ASSERT_FALSE(collector_.Enable()); ASSERT_FALSE(collector_.IsEnabled()); - ASSERT_EQ(std::string::npos, - logging_.log().find("Enabling kernel crash handling")); - ASSERT_NE(std::string::npos, - logging_.log().find("Kernel does not support crash dumping")); + ASSERT_TRUE(FindLog( + "Kernel does not support crash dumping")); ASSERT_EQ(s_crashes, 0); } @@ -79,8 +79,7 @@ TEST_F(KernelCollectorTest, EnableOK) { WriteStringToFile(test_kcrash_, ""); ASSERT_TRUE(collector_.Enable()); ASSERT_TRUE(collector_.IsEnabled()); - ASSERT_NE(std::string::npos, - logging_.log().find("Enabling kernel crash handling")); + ASSERT_TRUE(FindLog("Enabling kernel crash handling")); ASSERT_EQ(s_crashes, 0); } @@ -97,24 +96,22 @@ TEST_F(KernelCollectorTest, ClearPreservedDump) { TEST_F(KernelCollectorTest, CollectPreservedFileMissing) { ASSERT_FALSE(collector_.Collect()); - ASSERT_NE(logging_.log().find("Unable to read test/kcrash"), - std::string::npos); + ASSERT_TRUE(FindLog("Unable to read test/kcrash")); ASSERT_EQ(0, s_crashes); } TEST_F(KernelCollectorTest, CollectNoCrash) { WriteStringToFile(test_kcrash_, ""); ASSERT_FALSE(collector_.Collect()); - ASSERT_EQ(logging_.log().find("Collected kernel crash"), - std::string::npos); + ASSERT_FALSE(FindLog("Collected kernel crash")); ASSERT_EQ(0, s_crashes); } TEST_F(KernelCollectorTest, CollectBadDirectory) { WriteStringToFile(test_kcrash_, "something"); ASSERT_TRUE(collector_.Collect()); - ASSERT_NE(logging_.log().find( - "Unable to create appropriate crash directory"), std::string::npos); + ASSERT_TRUE(FindLog( + "Unable to create appropriate crash directory")); ASSERT_EQ(1, s_crashes); } @@ -135,7 +132,7 @@ TEST_F(KernelCollectorTest, CollectOptedOut) { SetUpSuccessfulCollect(); s_metrics = false; ASSERT_TRUE(collector_.Collect()); - ASSERT_NE(std::string::npos, logging_.log().find("(ignoring - no consent)")); + ASSERT_TRUE(FindLog("(ignoring - no consent)")); ASSERT_EQ(0, s_crashes); CheckPreservedDumpClear(); @@ -145,12 +142,13 @@ TEST_F(KernelCollectorTest, CollectOK) { SetUpSuccessfulCollect(); ASSERT_TRUE(collector_.Collect()); ASSERT_EQ(1, s_crashes); - ASSERT_NE(std::string::npos, logging_.log().find("(handling)")); + ASSERT_TRUE(FindLog("(handling)")); static const char kNamePrefix[] = "Stored kcrash to "; - size_t pos = logging_.log().find(kNamePrefix); + std::string log = chromeos::GetLog(); + size_t pos = log.find(kNamePrefix); ASSERT_NE(std::string::npos, pos); pos += strlen(kNamePrefix); - std::string filename = logging_.log().substr(pos, std::string::npos); + std::string filename = log.substr(pos, std::string::npos); // Take the name up until \n size_t end_pos = filename.find_first_of("\n"); ASSERT_NE(std::string::npos, end_pos); @@ -272,6 +270,6 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignature) { } int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); + SetUpTests(&argc, argv, false); return RUN_ALL_TESTS(); } diff --git a/crash_reporter/system_logging.cc b/crash_reporter/system_logging.cc deleted file mode 100644 index 04bb356ec..000000000 --- a/crash_reporter/system_logging.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "crash-reporter/system_logging.h" - -#include - -#include "base/stringprintf.h" - -std::string SystemLoggingImpl::identity_; - -SystemLoggingImpl::SystemLoggingImpl() : is_accumulating_(false) { -} - -SystemLoggingImpl::~SystemLoggingImpl() { -} - -void SystemLoggingImpl::Initialize(const char *ident) { - // Man page does not specify if openlog copies its string or assumes - // the pointer is always valid, so make its scope global. - identity_ = ident; - openlog(identity_.c_str(), LOG_PID, LOG_USER); -} - -void SystemLoggingImpl::LogWithLevel(int level, const char *format, - va_list arg_list) { - std::string message = StringPrintV(format, arg_list); - syslog(level, "%s", message.c_str()); - if (is_accumulating_) { - accumulator_.append(message); - accumulator_.push_back('\n'); - } -} - -void SystemLoggingImpl::LogInfo(const char *format, ...) { - va_list vl; - va_start(vl, format); - LogWithLevel(LOG_INFO, format, vl); - va_end(vl); -} - -void SystemLoggingImpl::LogWarning(const char *format, ...) { - va_list vl; - va_start(vl, format); - LogWithLevel(LOG_WARNING, format, vl); - va_end(vl); -} - -void SystemLoggingImpl::LogError(const char *format, ...) { - va_list vl; - va_start(vl, format); - LogWithLevel(LOG_ERR, format, vl); - va_end(vl); -} diff --git a/crash_reporter/system_logging.h b/crash_reporter/system_logging.h deleted file mode 100644 index d1f72a56e..000000000 --- a/crash_reporter/system_logging.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CRASH_REPORTER_SYSTEM_LOGGING_H_ -#define CRASH_REPORTER_SYSTEM_LOGGING_H_ - -#include -#include - -class SystemLogging { - public: - virtual void Initialize(const char *ident) = 0; - virtual void LogInfo(const char *format, ...) = 0; - virtual void LogWarning(const char *format, ...) = 0; - virtual void LogError(const char *format, ...) = 0; - virtual void set_accumulating(bool value) = 0; - virtual std::string get_accumulator() = 0; -}; - -// SystemLoggingImpl implements SystemLogging but adds the -// capability of accumulating the log to a STL string. -class SystemLoggingImpl : public SystemLogging { - public: - SystemLoggingImpl(); - virtual ~SystemLoggingImpl(); - virtual void Initialize(const char *ident); - virtual void LogInfo(const char *format, ...); - virtual void LogWarning(const char *format, ...); - virtual void LogError(const char *format, ...); - virtual void set_accumulating(bool value) { - is_accumulating_ = value; - } - virtual std::string get_accumulator() { - return accumulator_; - } - private: - static std::string identity_; - std::string accumulator_; - bool is_accumulating_; - void LogWithLevel(int level, const char *format, - va_list arg_list); -}; - -#endif // CRASH_REPORTER_SYSTEM_LOGGING_H_ diff --git a/crash_reporter/system_logging_mock.cc b/crash_reporter/system_logging_mock.cc deleted file mode 100644 index 04ce853b7..000000000 --- a/crash_reporter/system_logging_mock.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "base/string_util.h" -#include "crash-reporter/system_logging_mock.h" - -void SystemLoggingMock::LogInfo(const char *format, ...) { - va_list vl; - va_start(vl, format); - log_ += ident_ + "info: "; - StringAppendV(&log_, format, vl); - log_ += "\n"; - va_end(vl); -} - -void SystemLoggingMock::LogWarning(const char *format, ...) { - va_list vl; - va_start(vl, format); - log_ += ident_ + "warning: "; - StringAppendV(&log_, format, vl); - log_ += "\n"; - va_end(vl); -} - -void SystemLoggingMock::LogError(const char *format, ...) { - va_list vl; - va_start(vl, format); - log_ += ident_ + "error: "; - StringAppendV(&log_, format, vl); - log_ += "\n"; - va_end(vl); -} - -void SystemLoggingMock::set_accumulating(bool value) { -} - -std::string SystemLoggingMock::get_accumulator() { - return ""; -} diff --git a/crash_reporter/system_logging_mock.h b/crash_reporter/system_logging_mock.h deleted file mode 100644 index 56c6e5836..000000000 --- a/crash_reporter/system_logging_mock.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CRASH_REPORTER_SYSTEM_LOGGING_MOCK_H_ -#define CRASH_REPORTER_SYSTEM_LOGGING_MOCK_H_ - -#include - -#include "crash-reporter/system_logging.h" - -class SystemLoggingMock : public SystemLogging { - public: - void Initialize(const char *ident) {} - virtual void LogInfo(const char *format, ...); - virtual void LogWarning(const char *format, ...); - virtual void LogError(const char *format, ...); - virtual void set_accumulating(bool value); - virtual std::string get_accumulator(); - - const std::string &log() { return log_; } - - void clear() { log_.clear(); } - - private: - static std::string identity_; - std::string log_; - std::string ident_; -}; - -#endif // CRASH_REPORTER_SYSTEM_LOGGING_H_ diff --git a/crash_reporter/test_helpers.h b/crash_reporter/test_helpers.h deleted file mode 100644 index b51590181..000000000 --- a/crash_reporter/test_helpers.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef _CRASH_REPORTER_TEST_HELPERS_H_ -#define _CRASH_REPORTER_TEST_HELPERS_H_ - -#include "gtest/gtest.h" - -inline void ExpectFileEquals(const char *golden, - const char *file_path) { - std::string contents; - EXPECT_TRUE(file_util::ReadFileToString(FilePath(file_path), - &contents)); - EXPECT_EQ(golden, contents); -} - -#endif // _CRASH_REPORTER_TEST_HELPERS_H_ diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc index 1e1c7842a..ad7386f26 100644 --- a/crash_reporter/unclean_shutdown_collector.cc +++ b/crash_reporter/unclean_shutdown_collector.cc @@ -6,7 +6,6 @@ #include "base/file_util.h" #include "base/logging.h" -#include "crash-reporter/system_logging.h" static const char kUncleanShutdownFile[] = "/var/lib/crash_reporter/pending_clean_shutdown"; @@ -32,7 +31,7 @@ bool UncleanShutdownCollector::Enable() { FilePath file_path(unclean_shutdown_file_); file_util::CreateDirectory(file_path.DirName()); if (file_util::WriteFile(file_path, "", 0) != 0) { - logger_->LogError("Unable to create shutdown check file"); + LOG(ERROR) << "Unable to create shutdown check file"; return false; } return true; @@ -40,8 +39,8 @@ bool UncleanShutdownCollector::Enable() { bool UncleanShutdownCollector::DeleteUncleanShutdownFiles() { if (!file_util::Delete(FilePath(unclean_shutdown_file_), false)) { - logger_->LogError("Failed to delete unclean shutdown file %s", - unclean_shutdown_file_); + LOG(ERROR) << "Failed to delete unclean shutdown file " + << unclean_shutdown_file_; return false; } // Delete power manager trace files if they exist. @@ -55,7 +54,7 @@ bool UncleanShutdownCollector::Collect() { if (!file_util::PathExists(unclean_file_path)) { return false; } - logger_->LogWarning("Last shutdown was not clean"); + LOG(WARNING) << "Last shutdown was not clean"; if (DeadBatteryCausedUncleanShutdown()) { DeleteUncleanShutdownFiles(); return false; @@ -69,7 +68,7 @@ bool UncleanShutdownCollector::Collect() { } bool UncleanShutdownCollector::Disable() { - logger_->LogInfo("Clean shutdown signalled"); + LOG(INFO) << "Clean shutdown signalled"; return DeleteUncleanShutdownFiles(); } @@ -77,16 +76,16 @@ bool UncleanShutdownCollector::DeadBatteryCausedUncleanShutdown() { // Check for case of battery running out while suspended. if (file_util::PathExists(powerd_suspended_file_)) { - logger_->LogInfo("Unclean shutdown occurred while suspended. Not counting " - "toward unclean shutdown statistic."); + LOG(INFO) << "Unclean shutdown occurred while suspended. Not counting " + << "toward unclean shutdown statistic."; return true; } // Check for case of battery running out after resuming from a low-battery // suspend. if (file_util::PathExists(powerd_low_battery_file_)) { - logger_->LogInfo("Unclean shutdown occurred while running with battery " - "critically low. Not counting toward unclean shutdown " - "statistic."); + LOG(INFO) << "Unclean shutdown occurred while running with battery " + << "critically low. Not counting toward unclean shutdown " + << "statistic."; return true; } return false; diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc index 2cf9fb5d6..89b0ec9c6 100644 --- a/crash_reporter/unclean_shutdown_collector_test.cc +++ b/crash_reporter/unclean_shutdown_collector_test.cc @@ -6,8 +6,9 @@ #include "base/file_util.h" #include "base/string_util.h" +#include "chromeos/syslog_logging.h" +#include "chromeos/test_helpers.h" #include "crash-reporter/unclean_shutdown_collector.h" -#include "crash-reporter/system_logging_mock.h" #include "gflags/gflags.h" #include "gtest/gtest.h" @@ -18,6 +19,8 @@ static const char kTestLowBattery[] = "test/low_battery"; static const char kTestSuspended[] = "test/suspended"; static const char kTestUnclean[] = "test/unclean"; +using ::chromeos::FindLog; + void CountCrash() { ++s_crashes; } @@ -30,8 +33,7 @@ class UncleanShutdownCollectorTest : public ::testing::Test { void SetUp() { s_crashes = 0; collector_.Initialize(CountCrash, - IsMetrics, - &logging_); + IsMetrics); rmdir("test"); test_unclean_ = FilePath(kTestUnclean); collector_.unclean_shutdown_file_ = kTestUnclean; @@ -39,6 +41,7 @@ class UncleanShutdownCollectorTest : public ::testing::Test { // Set up alternate power manager tracing files as well collector_.powerd_suspended_file_ = FilePath(kTestSuspended); collector_.powerd_low_battery_file_ = FilePath(kTestLowBattery); + chromeos::ClearLog(); } protected: void WriteStringToFile(const FilePath &file_path, @@ -47,7 +50,6 @@ class UncleanShutdownCollectorTest : public ::testing::Test { file_util::WriteFile(file_path, data, strlen(data))); } - SystemLoggingMock logging_; UncleanShutdownCollector collector_; FilePath test_unclean_; }; @@ -66,8 +68,7 @@ TEST_F(UncleanShutdownCollectorTest, EnableWithParent) { TEST_F(UncleanShutdownCollectorTest, EnableCannotWrite) { collector_.unclean_shutdown_file_ = "/bad/path"; ASSERT_FALSE(collector_.Enable()); - ASSERT_NE(std::string::npos, - logging_.log().find("Unable to create shutdown check file")); + ASSERT_TRUE(FindLog("Unable to create shutdown check file")); } TEST_F(UncleanShutdownCollectorTest, CollectTrue) { @@ -76,8 +77,7 @@ TEST_F(UncleanShutdownCollectorTest, CollectTrue) { ASSERT_TRUE(collector_.Collect()); ASSERT_FALSE(file_util::PathExists(test_unclean_)); ASSERT_EQ(1, s_crashes); - ASSERT_NE(std::string::npos, - logging_.log().find("Last shutdown was not clean")); + ASSERT_TRUE(FindLog("Last shutdown was not clean")); } TEST_F(UncleanShutdownCollectorTest, CollectFalse) { @@ -93,9 +93,8 @@ TEST_F(UncleanShutdownCollectorTest, CollectDeadBatteryRunningLow) { ASSERT_FALSE(file_util::PathExists(test_unclean_)); ASSERT_FALSE(file_util::PathExists(collector_.powerd_low_battery_file_)); ASSERT_EQ(0, s_crashes); - ASSERT_NE(std::string::npos, - logging_.log().find("Unclean shutdown occurred while running with " - "battery critically low.")); + ASSERT_TRUE(FindLog( + "Unclean shutdown occurred while running with battery critically low.")); } TEST_F(UncleanShutdownCollectorTest, CollectDeadBatterySuspended) { @@ -106,8 +105,7 @@ TEST_F(UncleanShutdownCollectorTest, CollectDeadBatterySuspended) { ASSERT_FALSE(file_util::PathExists(test_unclean_)); ASSERT_FALSE(file_util::PathExists(collector_.powerd_suspended_file_)); ASSERT_EQ(0, s_crashes); - ASSERT_NE(std::string::npos, - logging_.log().find("Unclean shutdown occurred while suspended.")); + ASSERT_TRUE(FindLog("Unclean shutdown occurred while suspended.")); } TEST_F(UncleanShutdownCollectorTest, Disable) { @@ -130,6 +128,6 @@ TEST_F(UncleanShutdownCollectorTest, CantDisable) { } int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); + SetUpTests(&argc, argv, false); return RUN_ALL_TESTS(); } diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 6e6c76596..bfaaddfe1 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -16,7 +16,8 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" -#include "crash-reporter/system_logging.h" +#include "chromeos/process.h" +#include "chromeos/syslog_logging.h" #include "gflags/gflags.h" #pragma GCC diagnostic ignored "-Wstrict-aliasing" @@ -55,11 +56,9 @@ void UserCollector::Initialize( UserCollector::CountCrashFunction count_crash_function, const std::string &our_path, UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function, - SystemLogging *logger, bool generate_diagnostics) { CrashCollector::Initialize(count_crash_function, - is_feedback_allowed_function, - logger); + is_feedback_allowed_function); our_path_ = our_path; initialized_ = true; generate_diagnostics_ = generate_diagnostics; @@ -82,13 +81,13 @@ std::string UserCollector::GetPattern(bool enabled) const { bool UserCollector::SetUpInternal(bool enabled) { CHECK(initialized_); - logger_->LogInfo("%s user crash handling", - enabled ? "Enabling" : "Disabling"); + LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling"; + if (file_util::WriteFile(FilePath(core_pipe_limit_file_), kCorePipeLimit, strlen(kCorePipeLimit)) != static_cast(strlen(kCorePipeLimit))) { - logger_->LogError("Unable to write %s", core_pipe_limit_file_.c_str()); + LOG(ERROR) << "Unable to write " << core_pipe_limit_file_; return false; } std::string pattern = GetPattern(enabled); @@ -96,7 +95,7 @@ bool UserCollector::SetUpInternal(bool enabled) { pattern.c_str(), pattern.length()) != static_cast(pattern.length())) { - logger_->LogError("Unable to write %s", core_pattern_file_.c_str()); + LOG(ERROR) << "Unable to write " << core_pattern_file_; return false; } return true; @@ -115,8 +114,8 @@ bool UserCollector::GetSymlinkTarget(const FilePath &symlink, ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size); if (size < 0) { int saved_errno = errno; - logger_->LogError("Readlink failed on %s with %d", - symlink.value().c_str(), saved_errno); + LOG(ERROR) << "Readlink failed on " << symlink.value() << " with " + << saved_errno; return false; } buffer[size] = 0; @@ -142,19 +141,19 @@ bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid, FilePath process_path = GetProcessPath(pid); FilePath exe_path = process_path.Append("exe"); if (!GetSymlinkTarget(exe_path, &target)) { - logger_->LogInfo("GetSymlinkTarget failed - Path %s DirectoryExists: %d", - process_path.value().c_str(), - file_util::DirectoryExists(process_path)); + LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value() + << " DirectoryExists: " + << file_util::DirectoryExists(process_path); // Try to further diagnose exe readlink failure cause. struct stat buf; int stat_result = stat(exe_path.value().c_str(), &buf); int saved_errno = errno; if (stat_result < 0) { - logger_->LogInfo("stat %s failed: %d %d", exe_path.value().c_str(), - stat_result, saved_errno); + LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result + << " " << saved_errno; } else { - logger_->LogInfo("stat %s succeeded: st_mode=%d", - exe_path.value().c_str(), buf.st_mode); + LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode=" + << buf.st_mode; } return false; } @@ -198,13 +197,13 @@ bool UserCollector::GetIdFromStatus(const char *prefix, void UserCollector::EnqueueCollectionErrorLog(pid_t pid, const std::string &exec) { FilePath crash_path; - logger_->LogInfo("Writing conversion problems as separate crash report."); + LOG(INFO) << "Writing conversion problems as separate crash report."; if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) { - logger_->LogError("Could not even get log directory; out of space?"); + LOG(ERROR) << "Could not even get log directory; out of space?"; return; } std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); - std::string error_log = logger_->get_accumulator(); + std::string error_log = chromeos::GetLog(); FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog"); if (GetLogContents(FilePath(kDefaultLogConfig), kCollectionErrorSignature, diag_log_path)) { @@ -229,13 +228,12 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid, bool UserCollector::CopyOffProcFiles(pid_t pid, const FilePath &container_dir) { if (!file_util::CreateDirectory(container_dir)) { - logger_->LogError("Could not create %s", - container_dir.value().c_str()); + LOG(ERROR) << "Could not create " << container_dir.value().c_str(); return false; } FilePath process_path = GetProcessPath(pid); if (!file_util::PathExists(process_path)) { - logger_->LogError("Path %s does not exist", process_path.value().c_str()); + LOG(ERROR) << "Path " << process_path.value() << " does not exist"; return false; } static const char *proc_files[] = { @@ -248,7 +246,7 @@ bool UserCollector::CopyOffProcFiles(pid_t pid, for (unsigned i = 0; i < arraysize(proc_files); ++i) { if (!file_util::CopyFile(process_path.Append(proc_files[i]), container_dir.Append(proc_files[i]))) { - logger_->LogError("Could not copy %s file", proc_files[i]); + LOG(ERROR) << "Could not copy " << proc_files[i] << " file"; return false; } } @@ -261,26 +259,25 @@ bool UserCollector::GetCreatedCrashDirectory(pid_t pid, FilePath process_path = GetProcessPath(pid); std::string status; if (FLAGS_directory_failure) { - logger_->LogError("Purposefully failing to create spool directory"); + LOG(ERROR) << "Purposefully failing to create spool directory"; return false; } if (!file_util::ReadFileToString(process_path.Append("status"), &status)) { - logger_->LogError("Could not read status file"); - logger_->LogInfo("Path %s DirectoryExists: %d", - process_path.value().c_str(), - file_util::DirectoryExists(process_path)); + LOG(ERROR) << "Could not read status file"; + LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: " + << file_util::DirectoryExists(process_path); return false; } int process_euid; if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { - logger_->LogError("Could not find euid in status file"); + LOG(ERROR) << "Could not find euid in status file"; return false; } if (!GetCreatedCrashDirectoryByEuid(process_euid, crash_file_path, out_of_capacity)) { - logger_->LogError("Could not create crash directory"); + LOG(ERROR) << "Could not create crash directory"; return false; } return true; @@ -293,7 +290,7 @@ bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { return true; } - logger_->LogError("Could not write core file"); + LOG(ERROR) << "Could not write core file"; // If the file system was full, make sure we remove any remnants. file_util::Delete(core_path, false); return false; @@ -304,34 +301,32 @@ bool UserCollector::RunCoreToMinidump(const FilePath &core_path, const FilePath &minidump_path, const FilePath &temp_directory) { FilePath output_path = temp_directory.Append("output"); - std::vector core2md_arguments; - core2md_arguments.push_back(kCoreToMinidumpConverterPath); - core2md_arguments.push_back(core_path.value().c_str()); - core2md_arguments.push_back(procfs_directory.value().c_str()); - core2md_arguments.push_back(minidump_path.value().c_str()); + chromeos::ProcessImpl core2md; + core2md.RedirectOutput(output_path.value()); + core2md.AddArg(kCoreToMinidumpConverterPath); + core2md.AddArg(core_path.value()); + core2md.AddArg(procfs_directory.value()); - if (FLAGS_core2md_failure) { + if (!FLAGS_core2md_failure) { + core2md.AddArg(minidump_path.value()); + } else { // To test how core2md errors are propagaged, cause an error // by forgetting a required argument. - core2md_arguments.pop_back(); } - int errorlevel = ForkExecAndPipe(core2md_arguments, - output_path.value().c_str()); + int errorlevel = core2md.Run(); std::string output; file_util::ReadFileToString(output_path, &output); if (errorlevel != 0) { - logger_->LogError("Problem during %s [result=%d]: %s", - kCoreToMinidumpConverterPath, - errorlevel, - output.c_str()); + LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath + << " [result=" << errorlevel << "]: " << output; return false; } if (!file_util::PathExists(minidump_path)) { - logger_->LogError("Minidump file %s was not created", - minidump_path.value().c_str()); + LOG(ERROR) << "Minidump file " << minidump_path.value() + << " was not created"; return false; } return true; @@ -356,7 +351,7 @@ bool UserCollector::ConvertCoreToMinidump(pid_t pid, container_dir); // temporary directory if (conversion_result) { - logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str()); + LOG(INFO) << "Stored minidump to " << minidump_path.value(); } return conversion_result; @@ -367,7 +362,7 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid, bool *out_of_capacity) { FilePath crash_path; if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) { - logger_->LogError("Unable to find/create process-specific crash path"); + LOG(ERROR) << "Unable to find/create process-specific crash path"; return false; } @@ -390,8 +385,8 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid, if (!ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path)) { - logger_->LogInfo("Leaving core file at %s due to conversion error", - core_path.value().c_str()); + LOG(INFO) << "Leaving core file at " << core_path.value() + << " due to conversion error"; return false; } @@ -405,8 +400,8 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid, if (!file_util::PathExists(FilePath(kLeaveCoreFile))) { file_util::Delete(core_path, false); } else { - logger_->LogInfo("Leaving core file at %s due to developer image", - core_path.value().c_str()); + LOG(INFO) << "Leaving core file at " << core_path.value() + << " due to developer image"; } file_util::Delete(container_dir, true); @@ -429,7 +424,7 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &kernel_supplied_name)) { - logger_->LogError("Invalid parameter: --user=%s", crash_attributes.c_str()); + LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes; return false; } @@ -450,8 +445,8 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, FLAGS_filter_in != exec)) { // We use a different format message to make it more obvious in tests // which crashes are test generated and which are real. - logger_->LogWarning("Ignoring crash from %s[%d] while filter_in=%s.", - exec.c_str(), pid, FLAGS_filter_in.c_str()); + LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while " + << "filter_in=" << FLAGS_filter_in << "."; return true; } @@ -469,8 +464,8 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, handling_string = "ignoring - chrome crash"; } - logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", - exec.c_str(), pid, signal, handling_string); + LOG(WARNING) << "Received crash notification for " << exec << "[" << pid + << "] sig " << signal << " (" << handling_string << ")"; if (feedback) { count_crash_function_(); diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 21ef14b53..e46382cde 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -28,7 +28,6 @@ class UserCollector : public CrashCollector { void Initialize(CountCrashFunction count_crash, const std::string &our_path, IsFeedbackAllowedFunction is_metrics_allowed, - SystemLogging *logger, bool generate_diagnostics); virtual ~UserCollector(); diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 9a1d687ef..93a76adc4 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -5,9 +5,9 @@ #include #include "base/file_util.h" -#include "crash-reporter/system_logging_mock.h" +#include "chromeos/syslog_logging.h" +#include "chromeos/test_helpers.h" #include "crash-reporter/user_collector.h" -#include "crash-reporter/test_helpers.h" #include "gflags/gflags.h" #include "gtest/gtest.h" @@ -16,6 +16,8 @@ static bool s_metrics = false; static const char kFilePath[] = "/my/path"; +using chromeos::FindLog; + void CountCrash() { ++s_crashes; } @@ -30,13 +32,13 @@ class UserCollectorTest : public ::testing::Test { collector_.Initialize(CountCrash, kFilePath, IsMetrics, - &logging_, false); file_util::Delete(FilePath("test"), true); mkdir("test", 0777); collector_.set_core_pattern_file("test/core_pattern"); collector_.set_core_pipe_limit_file("test/core_pipe_limit"); pid_ = getpid(); + chromeos::ClearLog(); } protected: void ExpectFileEquals(const char *golden, @@ -47,7 +49,6 @@ class UserCollectorTest : public ::testing::Test { EXPECT_EQ(golden, contents); } - SystemLoggingMock logging_; UserCollector collector_; pid_t pid_; }; @@ -57,18 +58,15 @@ TEST_F(UserCollectorTest, EnableOK) { ExpectFileEquals("|/my/path --user=%p:%s:%e", "test/core_pattern"); ExpectFileEquals("4", "test/core_pipe_limit"); ASSERT_EQ(s_crashes, 0); - ASSERT_NE(logging_.log().find("Enabling user crash handling"), - std::string::npos); + EXPECT_TRUE(FindLog("Enabling user crash handling")); } TEST_F(UserCollectorTest, EnableNoPatternFileAccess) { collector_.set_core_pattern_file("/does_not_exist"); ASSERT_FALSE(collector_.Enable()); ASSERT_EQ(s_crashes, 0); - ASSERT_NE(logging_.log().find("Enabling user crash handling"), - std::string::npos); - ASSERT_NE(logging_.log().find("Unable to write /does_not_exist"), - std::string::npos); + EXPECT_TRUE(FindLog("Enabling user crash handling")); + EXPECT_TRUE(FindLog("Unable to write /does_not_exist")); } TEST_F(UserCollectorTest, EnableNoPipeLimitFileAccess) { @@ -78,28 +76,23 @@ TEST_F(UserCollectorTest, EnableNoPipeLimitFileAccess) { // Core pattern should not be written if we cannot access the pipe limit // or otherwise we may set a pattern that results in infinite recursion. ASSERT_FALSE(file_util::PathExists(FilePath("test/core_pattern"))); - ASSERT_NE(logging_.log().find("Enabling user crash handling"), - std::string::npos); - ASSERT_NE(logging_.log().find("Unable to write /does_not_exist"), - std::string::npos); + EXPECT_TRUE(FindLog("Enabling user crash handling")); + EXPECT_TRUE(FindLog("Unable to write /does_not_exist")); } TEST_F(UserCollectorTest, DisableOK) { ASSERT_TRUE(collector_.Disable()); ExpectFileEquals("core", "test/core_pattern"); ASSERT_EQ(s_crashes, 0); - ASSERT_NE(logging_.log().find("Disabling user crash handling"), - std::string::npos); + EXPECT_TRUE(FindLog("Disabling user crash handling")); } TEST_F(UserCollectorTest, DisableNoFileAccess) { collector_.set_core_pattern_file("/does_not_exist"); ASSERT_FALSE(collector_.Disable()); ASSERT_EQ(s_crashes, 0); - ASSERT_NE(logging_.log().find("Disabling user crash handling"), - std::string::npos); - ASSERT_NE(logging_.log().find("Unable to write /does_not_exist"), - std::string::npos); + EXPECT_TRUE(FindLog("Disabling user crash handling")); + EXPECT_TRUE(FindLog("Unable to write /does_not_exist")); } TEST_F(UserCollectorTest, ParseCrashAttributes) { @@ -132,40 +125,34 @@ TEST_F(UserCollectorTest, ParseCrashAttributes) { TEST_F(UserCollectorTest, HandleCrashWithoutMetrics) { s_metrics = false; collector_.HandleCrash("20:10:ignored", "foobar"); - ASSERT_NE(std::string::npos, - logging_.log().find( - "Received crash notification for foobar[20] sig 10")); + EXPECT_TRUE(FindLog( + "Received crash notification for foobar[20] sig 10")); ASSERT_EQ(s_crashes, 0); } TEST_F(UserCollectorTest, HandleNonChromeCrashWithMetrics) { s_metrics = true; collector_.HandleCrash("5:2:ignored", "chromeos-wm"); - ASSERT_NE(std::string::npos, - logging_.log().find( - "Received crash notification for chromeos-wm[5] sig 2")); + EXPECT_TRUE(FindLog( + "Received crash notification for chromeos-wm[5] sig 2")); ASSERT_EQ(s_crashes, 1); } TEST_F(UserCollectorTest, HandleChromeCrashWithMetrics) { s_metrics = true; collector_.HandleCrash("5:2:ignored", "chrome"); - ASSERT_NE(std::string::npos, - logging_.log().find( - "Received crash notification for chrome[5] sig 2")); - ASSERT_NE(std::string::npos, - logging_.log().find("(ignoring - chrome crash)")); + EXPECT_TRUE(FindLog( + "Received crash notification for chrome[5] sig 2")); + EXPECT_TRUE(FindLog("(ignoring - chrome crash)")); ASSERT_EQ(s_crashes, 0); } TEST_F(UserCollectorTest, HandleSuppliedChromeCrashWithMetrics) { s_metrics = true; collector_.HandleCrash("0:2:chrome", NULL); - ASSERT_NE(std::string::npos, - logging_.log().find( - "Received crash notification for supplied_chrome[0] sig 2")); - ASSERT_NE(std::string::npos, - logging_.log().find("(ignoring - chrome crash)")); + EXPECT_TRUE(FindLog( + "Received crash notification for supplied_chrome[0] sig 2")); + EXPECT_TRUE(FindLog("(ignoring - chrome crash)")); ASSERT_EQ(s_crashes, 0); } @@ -178,9 +165,8 @@ TEST_F(UserCollectorTest, GetSymlinkTarget) { FilePath result; ASSERT_FALSE(collector_.GetSymlinkTarget(FilePath("/does_not_exist"), &result)); - ASSERT_NE(std::string::npos, - logging_.log().find( - "Readlink failed on /does_not_exist with 2")); + ASSERT_TRUE(FindLog( + "Readlink failed on /does_not_exist with 2")); std::string long_link; for (int i = 0; i < 50; ++i) long_link += "0123456789"; @@ -201,23 +187,16 @@ TEST_F(UserCollectorTest, GetSymlinkTarget) { TEST_F(UserCollectorTest, GetExecutableBaseNameFromPid) { std::string base_name; EXPECT_FALSE(collector_.GetExecutableBaseNameFromPid(0, &base_name)); - EXPECT_NE(std::string::npos, - logging_.log().find( - "Readlink failed on /proc/0/exe with 2")); - EXPECT_NE(std::string::npos, - logging_.log().find( - "GetSymlinkTarget failed - Path " - "/proc/0 DirectoryExists: 0")); - EXPECT_NE(std::string::npos, - logging_.log().find( - "stat /proc/0/exe failed: -1 2")); + EXPECT_TRUE(FindLog( + "Readlink failed on /proc/0/exe with 2")); + EXPECT_TRUE(FindLog( + "GetSymlinkTarget failed - Path /proc/0 DirectoryExists: 0")); + EXPECT_TRUE(FindLog("stat /proc/0/exe failed: -1 2")); - logging_.clear(); + chromeos::ClearLog(); pid_t my_pid = getpid(); EXPECT_TRUE(collector_.GetExecutableBaseNameFromPid(my_pid, &base_name)); - EXPECT_EQ(std::string::npos, - logging_.log().find( - "Readlink failed")); + EXPECT_FALSE(FindLog("Readlink failed")); EXPECT_EQ("user_collector_test", base_name); } @@ -303,24 +282,19 @@ TEST_F(UserCollectorTest, GetUserInfoFromName) { TEST_F(UserCollectorTest, CopyOffProcFilesBadPath) { // Try a path that is not writable. ASSERT_FALSE(collector_.CopyOffProcFiles(pid_, FilePath("/bad/path"))); - ASSERT_NE(logging_.log().find( - "Could not create /bad/path"), - std::string::npos); + EXPECT_TRUE(FindLog("Could not create /bad/path")); } TEST_F(UserCollectorTest, CopyOffProcFilesBadPid) { FilePath container_path("test/container"); ASSERT_FALSE(collector_.CopyOffProcFiles(0, container_path)); - ASSERT_NE(logging_.log().find( - "Path /proc/0 does not exist"), - std::string::npos); + EXPECT_TRUE(FindLog("Path /proc/0 does not exist")); } TEST_F(UserCollectorTest, CopyOffProcFilesOK) { FilePath container_path("test/container"); ASSERT_TRUE(collector_.CopyOffProcFiles(pid_, container_path)); - ASSERT_EQ(logging_.log().find( - "Could not copy"), std::string::npos); + EXPECT_FALSE(FindLog("Could not copy")); static struct { const char *name; bool exists; @@ -342,6 +316,6 @@ TEST_F(UserCollectorTest, CopyOffProcFilesOK) { } int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); + SetUpTests(&argc, argv, false); return RUN_ALL_TESTS(); } From 1652fb2cc2bc6d08ac3719fd3d90b43485d539fe Mon Sep 17 00:00:00 2001 From: Thieu Le Date: Thu, 3 Mar 2011 12:14:43 -0800 Subject: [PATCH 045/225] Modify crash-reporter to collect crash information for unofficial builds. The original code review link is here: http://codereview.chromium.org/6559003/ BUG=chromium-os:12207 TESTS=logging_UserCrash, logging_CrashSender, manual test which involves purposely crashing a test to determine if minidump information is properly collected Change-Id: I20274050cd698434951c65612e246f510c8082cb TEST= Review URL: http://codereview.chromium.org/6615021 --- crash_reporter/crash_collector.cc | 5 +++++ crash_reporter/crash_collector.h | 3 +++ crash_reporter/user_collector.cc | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 9600b44a5..5247d916c 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -24,6 +24,7 @@ static const char kLsbRelease[] = "/etc/lsb-release"; static const char kShellPath[] = "/bin/sh"; static const char kSystemCrashPath[] = "/var/spool/crash"; static const char kUserCrashPath[] = "/home/chronos/user/crash"; +static const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress"; // Directory mode of the user crash spool directory. static const mode_t kUserCrashPathMode = 0755; @@ -349,3 +350,7 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, LOG(ERROR) << "Unable to write " << meta_path.value(); } } + +bool CrashCollector::IsCrashTestInProgress() { + return file_util::PathExists(FilePath(kCrashTestInProgressPath)); +} diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 3c09e2fdf..28503a12b 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -131,6 +131,9 @@ class CrashCollector { const std::string &exec_name, const std::string &payload_path); + // Returns true if the a crash test is currently running. + bool IsCrashTestInProgress(); + CountCrashFunction count_crash_function_; IsFeedbackAllowedFunction is_feedback_allowed_function_; std::string extra_metadata_; diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index bfaaddfe1..c166e125e 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -467,6 +467,13 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, LOG(WARNING) << "Received crash notification for " << exec << "[" << pid << "] sig " << signal << " (" << handling_string << ")"; + // For developer builds, we always want to keep the crash reports unless + // we're testing the crash facilities themselves. + if (file_util::PathExists(FilePath(kLeaveCoreFile)) && + !IsCrashTestInProgress()) { + feedback = true; + } + if (feedback) { count_crash_function_(); From 5d3a1a2c3543db21913f67cf240bad6c137063c9 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Wed, 16 Mar 2011 12:47:20 -0700 Subject: [PATCH 046/225] crash-reporter: keep ignoring chrome crashes even on dev builds Change-Id: Idd8859958e626dc782e511f35fca5d45a2977f53 BUG=chromium-os:12911 TEST=unit tests and UserCrash Review URL: http://codereview.chromium.org/6673002 --- crash_reporter/user_collector.cc | 62 +++++++++++++++++---------- crash_reporter/user_collector.h | 8 ++++ crash_reporter/user_collector_test.cc | 38 ++++++++++++++-- 3 files changed, 82 insertions(+), 26 deletions(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index c166e125e..a038e69db 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -415,6 +415,38 @@ bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes, return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name); } +bool UserCollector::ShouldDump(bool has_owner_consent, + bool is_developer, + bool is_crash_test_in_progress, + const std::string &exec, + std::string *reason) { + reason->clear(); + + // Treat Chrome crashes as if the user opted-out. We stop counting Chrome + // crashes towards user crashes, so user crashes really mean non-Chrome + // user-space crashes. + if (exec == "chrome" || exec == "supplied_chrome") { + *reason = "ignoring - chrome crash"; + return false; + } + + // For developer builds, we always want to keep the crash reports unless + // we're testing the crash facilities themselves. This overrides + // feedback. Crash sending still obeys consent. + if (is_developer && !is_crash_test_in_progress) { + *reason = "developer build - not testing - always dumping"; + return true; + } + + if (!has_owner_consent) { + *reason = "ignoring - no consent"; + return false; + } + + *reason = "handling"; + return true; +} + bool UserCollector::HandleCrash(const std::string &crash_attributes, const char *force_exec) { CHECK(initialized_); @@ -450,31 +482,17 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, return true; } - bool feedback = is_feedback_allowed_function_(); - const char *handling_string = "handling"; - if (!feedback) { - handling_string = "ignoring - no consent"; - } - - // Treat Chrome crashes as if the user opted-out. We stop counting Chrome - // crashes towards user crashes, so user crashes really mean non-Chrome - // user-space crashes. - if (exec == "chrome" || exec == "supplied_chrome") { - feedback = false; - handling_string = "ignoring - chrome crash"; - } + std::string reason; + bool dump = ShouldDump(is_feedback_allowed_function_(), + file_util::PathExists(FilePath(kLeaveCoreFile)), + IsCrashTestInProgress(), + exec, + &reason); LOG(WARNING) << "Received crash notification for " << exec << "[" << pid - << "] sig " << signal << " (" << handling_string << ")"; + << "] sig " << signal << " (" << reason << ")"; - // For developer builds, we always want to keep the crash reports unless - // we're testing the crash facilities themselves. - if (file_util::PathExists(FilePath(kLeaveCoreFile)) && - !IsCrashTestInProgress()) { - feedback = true; - } - - if (feedback) { + if (dump) { count_crash_function_(); if (generate_diagnostics_) { diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index e46382cde..a231dd646 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -63,6 +63,9 @@ class UserCollector : public CrashCollector { FRIEND_TEST(UserCollectorTest, GetSymlinkTarget); FRIEND_TEST(UserCollectorTest, GetUserInfoFromName); FRIEND_TEST(UserCollectorTest, ParseCrashAttributes); + FRIEND_TEST(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage); + FRIEND_TEST(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent); + FRIEND_TEST(UserCollectorTest, ShouldDumpUseConsentProductionImage); // Enumeration to pass to GetIdFromStatus. Must match the order // that the kernel lists IDs in the status file. @@ -114,6 +117,11 @@ class UserCollector : public CrashCollector { bool ParseCrashAttributes(const std::string &crash_attributes, pid_t *pid, int *signal, std::string *kernel_supplied_name); + bool ShouldDump(bool has_owner_consent, + bool is_developer, + bool is_crash_test_in_progress, + const std::string &exec, + std::string *reason); bool generate_diagnostics_; std::string core_pattern_file_; diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 93a76adc4..1a5c8c995 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -122,7 +122,37 @@ TEST_F(UserCollectorTest, ParseCrashAttributes) { &pid, &signal, &exec_name)); } -TEST_F(UserCollectorTest, HandleCrashWithoutMetrics) { +TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) { + std::string reason; + EXPECT_TRUE(collector_.ShouldDump(false, true, false, + "chrome-wm", &reason)); + EXPECT_EQ("developer build - not testing - always dumping", reason); + + // When running a crash test, behave as normal. + EXPECT_FALSE(collector_.ShouldDump(false, true, true, + "chrome-wm", &reason)); + EXPECT_EQ("ignoring - no consent", reason); +} + +TEST_F(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage) { + std::string reason; + EXPECT_FALSE(collector_.ShouldDump(false, true, false, + "chrome", &reason)); + EXPECT_EQ("ignoring - chrome crash", reason); +} + +TEST_F(UserCollectorTest, ShouldDumpUseConsentProductionImage) { + std::string result; + EXPECT_FALSE(collector_.ShouldDump(false, false, false, + "chrome-wm", &result)); + EXPECT_EQ("ignoring - no consent", result); + + EXPECT_TRUE(collector_.ShouldDump(true, false, false, + "chrome-wm", &result)); + EXPECT_EQ("handling", result); +} + +TEST_F(UserCollectorTest, HandleCrashWithoutConsent) { s_metrics = false; collector_.HandleCrash("20:10:ignored", "foobar"); EXPECT_TRUE(FindLog( @@ -130,7 +160,7 @@ TEST_F(UserCollectorTest, HandleCrashWithoutMetrics) { ASSERT_EQ(s_crashes, 0); } -TEST_F(UserCollectorTest, HandleNonChromeCrashWithMetrics) { +TEST_F(UserCollectorTest, HandleNonChromeCrashWithConsent) { s_metrics = true; collector_.HandleCrash("5:2:ignored", "chromeos-wm"); EXPECT_TRUE(FindLog( @@ -138,7 +168,7 @@ TEST_F(UserCollectorTest, HandleNonChromeCrashWithMetrics) { ASSERT_EQ(s_crashes, 1); } -TEST_F(UserCollectorTest, HandleChromeCrashWithMetrics) { +TEST_F(UserCollectorTest, HandleChromeCrashWithConsent) { s_metrics = true; collector_.HandleCrash("5:2:ignored", "chrome"); EXPECT_TRUE(FindLog( @@ -147,7 +177,7 @@ TEST_F(UserCollectorTest, HandleChromeCrashWithMetrics) { ASSERT_EQ(s_crashes, 0); } -TEST_F(UserCollectorTest, HandleSuppliedChromeCrashWithMetrics) { +TEST_F(UserCollectorTest, HandleSuppliedChromeCrashWithConsent) { s_metrics = true; collector_.HandleCrash("0:2:chrome", NULL); EXPECT_TRUE(FindLog( From ec951c7613876a1bb808c721fdd9e10fc2940021 Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Fri, 25 Mar 2011 15:27:12 -0700 Subject: [PATCH 047/225] crash-reporter: Don't send ps command lines with crash reporter When unable to generate a crash, don't show the commands' arguments with 'ps' because they may contain PII. Change-Id: I1e2f40e59535f4de08f43012727ab8e92bad8a0d BUG=chromium-os:13206 TEST=unit tests, logging_UserCrash autotest Review URL: http://codereview.chromium.org/6722017 --- crash_reporter/crash_reporter_logs.conf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 192198345..62c6216a0 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -19,8 +19,10 @@ update_engine:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/ # The following rule is used for generating additional diagnostics when # collection of user crashes fails. This output should not be too large -# as it is stored in memory. -crash_reporter-user-collection:echo "===ps output==="; ps auxw | tail -c 25000; echo "===dmesg output==="; dmesg | tail -c 25000; echo "===meminfo==="; cat /proc/meminfo +# as it is stored in memory. The output format specified for 'ps' is the +# same as with the "u" ("user-oriented") option, except it doesn't show +# the commands' arguments (i.e. "comm" instead of "command"). +crash_reporter-user-collection:echo "===ps output==="; ps axw -o user,pid,%cpu,%mem,vsz,rss,tname,stat,start_time,bsdtime,comm | tail -c 25000; echo "===dmesg output==="; dmesg | tail -c 25000; echo "===meminfo==="; cat /proc/meminfo # The following rules are only for testing purposes. crash_log_test:echo hello world From c7dcf3f52c0fda0cbcc5a17c961a3601899632ee Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Mon, 28 Mar 2011 13:41:07 -0700 Subject: [PATCH 048/225] Revert "crash-reporter: Don't send ps command lines with crash reporter" This reverts commit 78599c9f195a9c324d2b0eb5a048b677e6a4229c. BUG=chromium-os:13576 TEST= Review URL: http://codereview.chromium.org/6765002 Change-Id: I2c110e13199cc6d3741fa030065dbb405c2a049a --- crash_reporter/crash_reporter_logs.conf | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 62c6216a0..192198345 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -19,10 +19,8 @@ update_engine:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/ # The following rule is used for generating additional diagnostics when # collection of user crashes fails. This output should not be too large -# as it is stored in memory. The output format specified for 'ps' is the -# same as with the "u" ("user-oriented") option, except it doesn't show -# the commands' arguments (i.e. "comm" instead of "command"). -crash_reporter-user-collection:echo "===ps output==="; ps axw -o user,pid,%cpu,%mem,vsz,rss,tname,stat,start_time,bsdtime,comm | tail -c 25000; echo "===dmesg output==="; dmesg | tail -c 25000; echo "===meminfo==="; cat /proc/meminfo +# as it is stored in memory. +crash_reporter-user-collection:echo "===ps output==="; ps auxw | tail -c 25000; echo "===dmesg output==="; dmesg | tail -c 25000; echo "===meminfo==="; cat /proc/meminfo # The following rules are only for testing purposes. crash_log_test:echo hello world From d74cc09bbd6829535e1ce0b29bf2ef7de9fd8468 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 6 Apr 2011 10:47:01 -0700 Subject: [PATCH 049/225] Add ARM support Adds support for scanning ARM backtraces so that kernel crash reporting can work on ARM. BUG=chromium-os:12454 TEST=cat /sys/kernel/debug/preserved/kcrash > x crash_reporter -generate_kernel_signature x Verify that it finds a backtrace properly. FEATURES=test emerge-x86-mario crash-reporter - check all tests pass (this will run both ARM and X86 tests) Change-Id: I4dc6d7a2bae53d05883da9425ec8e9ac4a5c2bba Review URL: http://codereview.chromium.org/6599022 --- crash_reporter/kernel_collector.cc | 76 +++++++++++++-- crash_reporter/kernel_collector.h | 19 ++++ crash_reporter/kernel_collector_test.cc | 121 +++++++++++++++++++----- 3 files changed, 180 insertions(+), 36 deletions(-) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 652e7ff3a..56fa301f7 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -24,9 +24,31 @@ static const int kSignatureTimestampWindow = 2; // Kernel log timestamp regular expression. static const std::string kTimestampRegex("^<.*>\\[\\s*(\\d+\\.\\d+)\\]"); +/* + * These regular expressions enable to us capture the PC in a backtrace. + * The backtrace is obtained through dmesg or the kernel's preserved/kcrashmem + * feature. + * + * For ARM we see: + * "<5>[ 39.458982] PC is at write_breakme+0xd0/0x1b4" + * For x86: + * "<0>[ 37.474699] EIP: [<790ed488>] write_breakme+0x80/0x108 \ + * SS:ESP 0068:e9dd3efc + */ +static const char *s_pc_regex[] = { + 0, + " PC is at ([^\\+ ]+).*", + " EIP: \\[<.*>\\] ([^\\+ ]+).*", // X86 uses EIP for the program counter +}; + +COMPILE_ASSERT(arraysize(s_pc_regex) == KernelCollector::archCount, + missing_arch_pc_regexp); + KernelCollector::KernelCollector() : is_enabled_(false), preserved_dump_path_(kPreservedDumpPath) { + // We expect crash dumps in the format of the architecture we are built for. + arch_ = GetCompilerArch(); } KernelCollector::~KernelCollector() { @@ -47,7 +69,12 @@ bool KernelCollector::LoadPreservedDump(std::string *contents) { } bool KernelCollector::Enable() { - if (!file_util::PathExists(preserved_dump_path_)) { + if (arch_ == archUnknown || arch_ >= archCount || + s_pc_regex[arch_] == NULL) { + LOG(WARNING) << "KernelCollector does not understand this architecture"; + return false; + } + else if (!file_util::PathExists(preserved_dump_path_)) { LOG(WARNING) << "Kernel does not support crash dumping"; return false; } @@ -90,11 +117,21 @@ void KernelCollector::ProcessStackTrace( unsigned *hash, float *last_stack_timestamp) { pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE()); - pcrecpp::RE stack_trace_start_re(kTimestampRegex + " Call Trace:$"); + pcrecpp::RE stack_trace_start_re(kTimestampRegex + + " (Call Trace|Backtrace):$"); + + // For ARM: + // <4>[ 3498.731164] [] (__bug+0x20/0x2c) from [] + // (write_breakme+0xdc/0x1bc) + // + // For X86: // Match lines such as the following and grab out "error_code". - // <4>[ 6066.849504] [<7937bcee>] error_code+0x66/0x6c + // <4>[ 6066.849504] [<7937bcee>] ? error_code+0x66/0x6c + // The ? may or may not be present pcrecpp::RE stack_entry_re(kTimestampRegex + - " \\[<.*>\\]([\\s\\?]+)([^\\+ ]+)"); + "\\s+\\[<[[:xdigit:]]+>\\]" // Matches " [<7937bcee>]" + "([\\s\\?(]+)" // Matches " ? (" (ARM) or " ? " (X86) + "([^\\+ )]+)"); // Matches until delimiter reached std::string line; std::string hashable; @@ -137,14 +174,33 @@ void KernelCollector::ProcessStackTrace( } } +enum KernelCollector::ArchKind KernelCollector::GetCompilerArch(void) +{ +#if defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY) + return archArm; +#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY) + return archX86; +#else + return archUnknown; +#endif +} + +void KernelCollector::SetArch(enum ArchKind arch) +{ + arch_ = arch; +} + bool KernelCollector::FindCrashingFunction( - pcrecpp::StringPiece kernel_dump, - bool print_diagnostics, - float stack_trace_timestamp, - std::string *crashing_function) { - pcrecpp::RE eip_re(kTimestampRegex + " EIP: \\[<.*>\\] ([^\\+ ]+).*", - pcrecpp::MULTILINE()); + pcrecpp::StringPiece kernel_dump, + bool print_diagnostics, + float stack_trace_timestamp, + std::string *crashing_function) { float timestamp = 0; + + // Use the correct regex for this architecture. + pcrecpp::RE eip_re(kTimestampRegex + s_pc_regex[arch_], + pcrecpp::MULTILINE()); + while (eip_re.FindAndConsume(&kernel_dump, ×tamp, crashing_function)) { if (print_diagnostics) { printf("@%f: found crashing function %s\n", diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index 708fd1b9e..04f3bdd65 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -18,6 +18,15 @@ class FilePath; // Kernel crash collector. class KernelCollector : public CrashCollector { public: + // Enumeration to specify architecture type. + enum ArchKind { + archUnknown, + archArm, + archX86, + + archCount // Number of architectures. + }; + KernelCollector(); virtual ~KernelCollector(); @@ -41,6 +50,10 @@ class KernelCollector : public CrashCollector { std::string *kernel_signature, bool print_diagnostics); + // Set the architecture of the crash dumps we are looking at. + void SetArch(enum ArchKind arch); + enum ArchKind GetArch() { return arch_; } + private: friend class KernelCollectorTest; FRIEND_TEST(KernelCollectorTest, ClearPreservedDump); @@ -62,9 +75,15 @@ class KernelCollector : public CrashCollector { bool print_diagnostics, std::string *panic_message); + // Returns the architecture kind for which we are built - enum ArchKind. + enum ArchKind GetCompilerArch(void); + bool is_enabled_; FilePath preserved_dump_path_; static const char kClearingSequence[]; + + // The architecture of kernel dump strings we are working with. + enum ArchKind arch_; }; #endif // _CRASH_REPORTER_KERNEL_COLLECTOR_H_ diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index f7c88f09a..08e31698b 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -50,11 +50,17 @@ class KernelCollectorTest : public ::testing::Test { void SetUpSuccessfulCollect(); void CheckPreservedDumpClear(); + void ComputeKernelStackSignatureCommon(); KernelCollector collector_; FilePath test_kcrash_; }; +TEST_F(KernelCollectorTest, ComputeKernelStackSignatureBase) { + // Make sure the normal build architecture is detected + EXPECT_TRUE(collector_.GetArch() != KernelCollector::archUnknown); +} + TEST_F(KernelCollectorTest, LoadPreservedDump) { ASSERT_FALSE(file_util::PathExists(test_kcrash_)); std::string dump; @@ -162,7 +168,92 @@ TEST_F(KernelCollectorTest, CollectOK) { CheckPreservedDumpClear(); } -TEST_F(KernelCollectorTest, ComputeKernelStackSignature) { +// Perform tests which are common across architectures +void KernelCollectorTest::ComputeKernelStackSignatureCommon() { + std::string signature; + + const char kStackButNoPC[] = + "<4>[ 6066.829029] [<790340af>] __do_softirq+0xa6/0x143\n"; + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kStackButNoPC, &signature, false)); + EXPECT_EQ("kernel--83615F0A", signature); + + const char kMissingEverything[] = + "<4>[ 6066.829029] [<790340af>] ? __do_softirq+0xa6/0x143\n"; + EXPECT_FALSE( + collector_.ComputeKernelStackSignature(kMissingEverything, + &signature, + false)); + + // Long message. + const char kTruncatedMessage[] = + "<0>[ 87.485611] Kernel panic - not syncing: 01234567890123456789" + "01234567890123456789X\n"; + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kTruncatedMessage, + &signature, + false)); + EXPECT_EQ("kernel-0123456789012345678901234567890123456789-00000000", + signature); +} + +TEST_F(KernelCollectorTest, ComputeKernelStackSignatureARM) { + const char kBugToPanic[] = + "<5>[ 123.412524] Modules linked in:\n" + "<5>[ 123.412534] CPU: 0 Tainted: G W " + "(2.6.37-01030-g51cee64 #153)\n" + "<5>[ 123.412552] PC is at write_breakme+0xd0/0x1b4\n" + "<5>[ 123.412560] LR is at write_breakme+0xc8/0x1b4\n" + "<5>[ 123.412569] pc : [] lr : [] " + "psr: 60000013\n" + "<5>[ 123.412574] sp : f4e0ded8 ip : c04d104c fp : 000e45e0\n" + "<5>[ 123.412581] r10: 400ff000 r9 : f4e0c000 r8 : 00000004\n" + "<5>[ 123.412589] r7 : f4e0df80 r6 : f4820c80 r5 : 00000004 " + "r4 : f4e0dee8\n" + "<5>[ 123.412598] r3 : 00000000 r2 : f4e0decc r1 : c05f88a9 " + "r0 : 00000039\n" + "<5>[ 123.412608] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA " + "ARM Segment user\n" + "<5>[ 123.412617] Control: 10c53c7d Table: 34dcc04a DAC: 00000015\n" + "<0>[ 123.412626] Process bash (pid: 1014, stack limit = 0xf4e0c2f8)\n" + "<0>[ 123.412634] Stack: (0xf4e0ded8 to 0xf4e0e000)\n" + "<0>[ 123.412641] dec0: " + " f4e0dee8 c0183678\n" + "<0>[ 123.412654] dee0: 00000000 00000000 00677562 0000081f c06a6a78 " + "400ff000 f4e0dfb0 00000000\n" + "<0>[ 123.412666] df00: bec7ab44 000b1719 bec7ab0c c004f498 bec7a314 " + "c024acc8 00000001 c018359c\n" + "<0>[ 123.412679] df20: f4e0df34 c04d10fc f5803c80 271beb39 000e45e0 " + "f5803c80 c018359c c017bfe0\n" + "<0>[ 123.412691] df40: 00000004 f4820c80 400ff000 f4e0df80 00000004 " + "f4e0c000 00000000 c01383e4\n" + "<0>[ 123.412703] df60: f4820c80 400ff000 f4820c80 400ff000 00000000 " + "00000000 00000004 c0138578\n" + "<0>[ 123.412715] df80: 00000000 00000000 00000004 00000000 00000004 " + "402f95d0 00000004 00000004\n" + "<0>[ 123.412727] dfa0: c0054984 c00547c0 00000004 402f95d0 00000001 " + "400ff000 00000004 00000000\n" + "<0>[ 123.412739] dfc0: 00000004 402f95d0 00000004 00000004 400ff000 " + "000c194c bec7ab58 000e45e0\n" + "<0>[ 123.412751] dfe0: 00000000 bec7aad8 40232520 40284e9c 60000010 " + "00000001 00000000 00000000\n" + "<5>[ 39.496577] Backtrace:\n" + "<5>[ 123.412782] [] (__bug+0x20/0x2c) from [] " + "(write_breakme+0xdc/0x1bc)\n" + "<5>[ 123.412798] [] (write_breakme+0xdc/0x1bc) from " + "[] (proc_reg_write+0x88/0x9c)\n"; + std::string signature; + + collector_.SetArch(KernelCollector::archArm); + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false)); + EXPECT_EQ("kernel-write_breakme-97D3E92F", signature); + + ComputeKernelStackSignatureCommon(); +} + + +TEST_F(KernelCollectorTest, ComputeKernelStackSignatureX86) { const char kBugToPanic[] = "<4>[ 6066.829029] [<79039d16>] ? run_timer_softirq+0x165/0x1e6\n" "<4>[ 6066.829029] [<790340af>] ignore_old_stack+0xa6/0x143\n" @@ -178,6 +269,8 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignature) { "<4>[ 6066.949971] [<7937c5c5>] oops_end+0x73/0x81\n" "<4>[ 6066.950208] [<7901b260>] no_context+0x10d/0x117\n"; std::string signature; + + collector_.SetArch(KernelCollector::archX86); EXPECT_TRUE( collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false)); EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-DE253569", signature); @@ -188,19 +281,6 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignature) { collector_.ComputeKernelStackSignature(kPCButNoStack, &signature, false)); EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-00000000", signature); - const char kStackButNoPC[] = - "<4>[ 6066.829029] [<790340af>] __do_softirq+0xa6/0x143\n"; - EXPECT_TRUE( - collector_.ComputeKernelStackSignature(kStackButNoPC, &signature, false)); - EXPECT_EQ("kernel--83615F0A", signature); - - const char kMissingEverything[] = - "<4>[ 6066.829029] [<790340af>] ? __do_softirq+0xa6/0x143\n"; - EXPECT_FALSE( - collector_.ComputeKernelStackSignature(kMissingEverything, - &signature, - false)); - const char kBreakmeBug[] = "<4>[ 180.492137] [<790970c6>] ? handle_mm_fault+0x67f/0x96d\n" "<4>[ 180.492137] [<790dcdfe>] ? proc_reg_write+0x5f/0x73\n" @@ -255,18 +335,7 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignature) { &signature, false)); EXPECT_EQ("kernel-Testing panic-E0FC3552", signature); - - // Long message. - const char kTruncatedMessage[] = - "<0>[ 87.485611] Kernel panic - not syncing: 01234567890123456789" - "01234567890123456789X\n"; - EXPECT_TRUE( - collector_.ComputeKernelStackSignature(kTruncatedMessage, - &signature, - false)); - EXPECT_EQ("kernel-0123456789012345678901234567890123456789-00000000", - signature); - + ComputeKernelStackSignatureCommon(); } int main(int argc, char **argv) { From 1e6b8bdbba944299b31b977297b3ce1aeb2fee7d Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Thu, 7 Apr 2011 09:40:05 -0700 Subject: [PATCH 050/225] Strip MAC addresses from dmesg sent w/ kcrash files. BUG=chromium-os:13579 TEST=Ran the kernel_collector_test rm -f /build/x86-mario/tmp/portage/chromeos-base/crash-reporter-9999/.tested; cros_workon_make --test --board=x86-mario crash-reporter [ RUN ] KernelCollectorTest.StripSensitiveDataBasic [ OK ] KernelCollectorTest.StripSensitiveDataBasic (1 ms) [ RUN ] KernelCollectorTest.StripSensitiveDataBulk [ OK ] KernelCollectorTest.StripSensitiveDataBulk (2 ms) [ RUN ] KernelCollectorTest.StripSensitiveDataSample [ OK ] KernelCollectorTest.StripSensitiveDataSample (0 ms) Review URL: http://codereview.chromium.org/6777001 Change-Id: Ie2cd3d007f9ee2fb877d28280cfe62748c108bd2 --- crash_reporter/kernel_collector.cc | 76 +++++++++++++++ crash_reporter/kernel_collector.h | 4 + crash_reporter/kernel_collector_test.cc | 117 ++++++++++++++++++++++++ 3 files changed, 197 insertions(+) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 56fa301f7..529e35f57 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -68,6 +68,81 @@ bool KernelCollector::LoadPreservedDump(std::string *contents) { return true; } +void KernelCollector::StripSensitiveData(std::string *kernel_dump) { + // Strip any data that the user might not want sent up to the crash servers. + // We'll read in from kernel_dump and also place our output there. + // + // At the moment, the only sensitive data we strip is MAC addresses. + + // Get rid of things that look like MAC addresses, since they could possibly + // give information about where someone has been. This is strings that look + // like this: 11:22:33:44:55:66 + // Complications: + // - Within a given kernel_dump, want to be able to tell when the same MAC + // was used more than once. Thus, we'll consistently replace the first + // MAC found with 00:00:00:00:00:01, the second with ...:02, etc. + // - ACPI commands look like MAC addresses. We'll specifically avoid getting + // rid of those. + std::ostringstream result; + std::string pre_mac_str; + std::string mac_str; + std::map mac_map; + pcrecpp::StringPiece input(*kernel_dump); + + // This RE will find the next MAC address and can return us the data preceding + // the MAC and the MAC itself. + pcrecpp::RE mac_re("(.*?)(" + "[0-9a-fA-F][0-9a-fA-F]:" + "[0-9a-fA-F][0-9a-fA-F]:" + "[0-9a-fA-F][0-9a-fA-F]:" + "[0-9a-fA-F][0-9a-fA-F]:" + "[0-9a-fA-F][0-9a-fA-F]:" + "[0-9a-fA-F][0-9a-fA-F])", + pcrecpp::RE_Options() + .set_multiline(true) + .set_dotall(true)); + + // This RE will identify when the 'pre_mac_str' shows that the MAC address + // was really an ACPI cmd. The full string looks like this: + // ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES) filtered out + pcrecpp::RE acpi_re("ACPI cmd ef/$", + pcrecpp::RE_Options() + .set_multiline(true) + .set_dotall(true)); + + // Keep consuming, building up a result string as we go. + while (mac_re.Consume(&input, &pre_mac_str, &mac_str)) { + if (acpi_re.PartialMatch(pre_mac_str)) { + // We really saw an ACPI command; add to result w/ no stripping. + result << pre_mac_str << mac_str; + } else { + // Found a MAC address; look up in our hash for the mapping. + std::string replacement_mac = mac_map[mac_str]; + if (replacement_mac == "") { + // It wasn't present, so build up a replacement string. + int mac_id = mac_map.size(); + + // Handle up to 2^32 unique MAC address; overkill, but doesn't hurt. + replacement_mac = StringPrintf("00:00:%02x:%02x:%02x:%02x", + (mac_id & 0xff000000) >> 24, + (mac_id & 0x00ff0000) >> 16, + (mac_id & 0x0000ff00) >> 8, + (mac_id & 0x000000ff)); + mac_map[mac_str] = replacement_mac; + } + + // Dump the string before the MAC and the fake MAC address into result. + result << pre_mac_str << replacement_mac; + } + } + + // One last bit of data might still be in the input. + result << input; + + // We'll just assign right back to kernel_dump. + *kernel_dump = result.str(); +} + bool KernelCollector::Enable() { if (arch_ == archUnknown || arch_ >= archCount || s_pc_regex[arch_] == NULL) { @@ -300,6 +375,7 @@ bool KernelCollector::Collect() { if (!LoadPreservedDump(&kernel_dump)) { return false; } + StripSensitiveData(&kernel_dump); if (kernel_dump.empty()) { return false; } diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index 04f3bdd65..1c86f40c5 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -58,10 +58,14 @@ class KernelCollector : public CrashCollector { friend class KernelCollectorTest; FRIEND_TEST(KernelCollectorTest, ClearPreservedDump); FRIEND_TEST(KernelCollectorTest, LoadPreservedDump); + FRIEND_TEST(KernelCollectorTest, StripSensitiveDataBasic); + FRIEND_TEST(KernelCollectorTest, StripSensitiveDataBulk); + FRIEND_TEST(KernelCollectorTest, StripSensitiveDataSample); FRIEND_TEST(KernelCollectorTest, CollectOK); bool LoadPreservedDump(std::string *contents); bool ClearPreservedDump(); + void StripSensitiveData(std::string *kernel_dump); void ProcessStackTrace(pcrecpp::StringPiece kernel_dump, bool print_diagnostics, diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 08e31698b..bfa2f04d4 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -100,6 +100,123 @@ TEST_F(KernelCollectorTest, ClearPreservedDump) { ASSERT_EQ(KernelCollector::kClearingSequence, dump); } +TEST_F(KernelCollectorTest, StripSensitiveDataBasic) { + // Basic tests of StripSensitiveData... + + // Make sure we work OK with a string w/ no MAC addresses. + const std::string kCrashWithNoMacsOrig = + "<7>[111566.131728] PM: Entering mem sleep\n"; + std::string crash_with_no_macs(kCrashWithNoMacsOrig); + collector_.StripSensitiveData(&crash_with_no_macs); + EXPECT_EQ(kCrashWithNoMacsOrig, crash_with_no_macs); + + // Make sure that we handle the case where there's nothing before/after the + // MAC address. + const std::string kJustAMacOrig = + "11:22:33:44:55:66"; + const std::string kJustAMacStripped = + "00:00:00:00:00:01"; + std::string just_a_mac(kJustAMacOrig); + collector_.StripSensitiveData(&just_a_mac); + EXPECT_EQ(kJustAMacStripped, just_a_mac); + + // Test MAC addresses crammed together to make sure it gets both of them. + // + // I'm not sure that the code does ideal on these two test cases (they don't + // look like two MAC addresses to me), but since we don't see them I think + // it's OK to behave as shown here. + const std::string kCrammedMacs1Orig = + "11:22:33:44:55:66:11:22:33:44:55:66"; + const std::string kCrammedMacs1Stripped = + "00:00:00:00:00:01:00:00:00:00:00:01"; + std::string crammed_macs_1(kCrammedMacs1Orig); + collector_.StripSensitiveData(&crammed_macs_1); + EXPECT_EQ(kCrammedMacs1Stripped, crammed_macs_1); + + const std::string kCrammedMacs2Orig = + "11:22:33:44:55:6611:22:33:44:55:66"; + const std::string kCrammedMacs2Stripped = + "00:00:00:00:00:0100:00:00:00:00:01"; + std::string crammed_macs_2(kCrammedMacs2Orig); + collector_.StripSensitiveData(&crammed_macs_2); + EXPECT_EQ(kCrammedMacs2Stripped, crammed_macs_2); + + // Test case-sensitiveness (we shouldn't be case-senstive). + const std::string kCapsMacOrig = + "AA:BB:CC:DD:EE:FF"; + const std::string kCapsMacStripped = + "00:00:00:00:00:01"; + std::string caps_mac(kCapsMacOrig); + collector_.StripSensitiveData(&caps_mac); + EXPECT_EQ(kCapsMacStripped, caps_mac); + + const std::string kLowerMacOrig = + "aa:bb:cc:dd:ee:ff"; + const std::string kLowerMacStripped = + "00:00:00:00:00:01"; + std::string lower_mac(kLowerMacOrig); + collector_.StripSensitiveData(&lower_mac); + EXPECT_EQ(kLowerMacStripped, lower_mac); +} + +TEST_F(KernelCollectorTest, StripSensitiveDataBulk) { + // Test calling StripSensitiveData w/ lots of MAC addresses in the "log". + + // Test that stripping code handles more than 256 unique MAC addresses, since + // that overflows past the last byte... + // We'll write up some code that generates 258 unique MAC addresses. Sorta + // cheating since the code is very similar to the current code in + // StripSensitiveData(), but would catch if someone changed that later. + std::string lotsa_macs_orig; + std::string lotsa_macs_stripped; + int i; + for (i = 0; i < 258; i++) { + lotsa_macs_orig += StringPrintf(" 11:11:11:11:%02X:%02x", + (i & 0xff00) >> 8, i & 0x00ff); + lotsa_macs_stripped += StringPrintf(" 00:00:00:00:%02X:%02x", + ((i+1) & 0xff00) >> 8, (i+1) & 0x00ff); + } + std::string lotsa_macs(lotsa_macs_orig); + collector_.StripSensitiveData(&lotsa_macs); + EXPECT_EQ(lotsa_macs_stripped, lotsa_macs); +} + +TEST_F(KernelCollectorTest, StripSensitiveDataSample) { + // Test calling StripSensitiveData w/ some actual lines from a real crash; + // included two MAC addresses (though replaced them with some bogusness). + const std::string kCrashWithMacsOrig = + "<6>[111567.195339] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES)" + " filtered out\n" + "<7>[108539.540144] wlan0: authenticate with 11:22:33:44:55:66 (try 1)\n" + "<7>[108539.554973] wlan0: associate with 11:22:33:44:55:66 (try 1)\n" + "<6>[110136.587583] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2," + " QCUSBNet Ethernet Device, 99:88:77:66:55:44\n" + "<7>[110964.314648] wlan0: deauthenticated from 11:22:33:44:55:66" + " (Reason: 6)\n" + "<7>[110964.325057] phy0: Removed STA 11:22:33:44:55:66\n" + "<7>[110964.325115] phy0: Destroyed STA 11:22:33:44:55:66\n" + "<6>[110969.219172] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2," + " QCUSBNet Ethernet Device, 99:88:77:66:55:44\n" + "<7>[111566.131728] PM: Entering mem sleep\n"; + const std::string kCrashWithMacsStripped = + "<6>[111567.195339] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES)" + " filtered out\n" + "<7>[108539.540144] wlan0: authenticate with 00:00:00:00:00:01 (try 1)\n" + "<7>[108539.554973] wlan0: associate with 00:00:00:00:00:01 (try 1)\n" + "<6>[110136.587583] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2," + " QCUSBNet Ethernet Device, 00:00:00:00:00:02\n" + "<7>[110964.314648] wlan0: deauthenticated from 00:00:00:00:00:01" + " (Reason: 6)\n" + "<7>[110964.325057] phy0: Removed STA 00:00:00:00:00:01\n" + "<7>[110964.325115] phy0: Destroyed STA 00:00:00:00:00:01\n" + "<6>[110969.219172] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2," + " QCUSBNet Ethernet Device, 00:00:00:00:00:02\n" + "<7>[111566.131728] PM: Entering mem sleep\n"; + std::string crash_with_macs(kCrashWithMacsOrig); + collector_.StripSensitiveData(&crash_with_macs); + EXPECT_EQ(kCrashWithMacsStripped, crash_with_macs); +} + TEST_F(KernelCollectorTest, CollectPreservedFileMissing) { ASSERT_FALSE(collector_.Collect()); ASSERT_TRUE(FindLog("Unable to read test/kcrash")); From c046f57ef36b14d01e49ae004c85bc7095a58430 Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Thu, 7 Apr 2011 14:42:14 -0700 Subject: [PATCH 051/225] Revert "Revert "crash-reporter: Don't send ps command lines with crash reporter"" This reverts commit 59d8695734dac2ce70b17d2a58049083e65e997d. My original commit did not actually break anything, so this just puts it back. BUG=chromium-os:13576 TEST=unit tests, logging_{UserCrash,CrashSender} autotests Review URL: http://codereview.chromium.org/6765017 Change-Id: I5b9ebb2b210b86fb4efaf49c71dcd2562805c973 --- crash_reporter/crash_reporter_logs.conf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 192198345..62c6216a0 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -19,8 +19,10 @@ update_engine:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/ # The following rule is used for generating additional diagnostics when # collection of user crashes fails. This output should not be too large -# as it is stored in memory. -crash_reporter-user-collection:echo "===ps output==="; ps auxw | tail -c 25000; echo "===dmesg output==="; dmesg | tail -c 25000; echo "===meminfo==="; cat /proc/meminfo +# as it is stored in memory. The output format specified for 'ps' is the +# same as with the "u" ("user-oriented") option, except it doesn't show +# the commands' arguments (i.e. "comm" instead of "command"). +crash_reporter-user-collection:echo "===ps output==="; ps axw -o user,pid,%cpu,%mem,vsz,rss,tname,stat,start_time,bsdtime,comm | tail -c 25000; echo "===dmesg output==="; dmesg | tail -c 25000; echo "===meminfo==="; cat /proc/meminfo # The following rules are only for testing purposes. crash_log_test:echo hello world From 57a942f51672a0e2559139e90b4b0829a2d476ca Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Fri, 15 Apr 2011 17:18:27 -0700 Subject: [PATCH 052/225] crash-reporter: Create a list_proxies command Create a list_proxies command to be used by crash_sender to determine the correct proxy for a URL. This uses D-Bus to get them from either the browser or the session manager. BUG=chromium-os:6828 TEST=Ran list_proxies command manually to test response for various URLs: "http://...", "https://...", empty/NULL, and invalid (eg. just "blah.com"). Also did this for the various proxy settings in the browser: direct, manual proxy configuration, and automatic proxy configuration. Ran list_proxies command using invalid D-Bus method name for proxy_resolver.StartMonitoring() to make sure timeout works. Ran a modified crash_sender manually to make sure it passed the appropriate "--proxy" arguments correctly. Review URL: http://codereview.chromium.org/6730021 Change-Id: I738a4544f90218ffc7c2d36547de49b462ca749e --- crash_reporter/Makefile | 17 ++- crash_reporter/list_proxies.cc | 229 +++++++++++++++++++++++++++++++++ 2 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 crash_reporter/list_proxies.cc diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index b649bea24..9658e7713 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -3,12 +3,15 @@ # found in the LICENSE file. CRASH_REPORTER = crash_reporter -REPORTER_BINS = $(CRASH_REPORTER) +LIST_PROXIES_BIN = list_proxies +REPORTER_BINS = $(CRASH_REPORTER) $(LIST_PROXIES_BIN) CRASH_OBJS = \ crash_collector.o \ kernel_collector.o \ unclean_shutdown_collector.o \ user_collector.o +LIST_PROXIES_OBJS = list_proxies.o +LIST_PROXIES_PKGS = dbus-1 dbus-glib-1 TEST_OBJS = $(CRASH_OBJS) TEST_BINS = \ crash_collector_test \ @@ -19,11 +22,14 @@ TEST_BINS = \ LDCONFIG = $(shell $(PKG_CONFIG) --libs libpcrecpp) # -lglib-2.0 is needed by libbase.a now. -COMMON_LIBS = -lchromeos -lbase -lpthread -lglib-2.0 -lgflags -lrt $(LDCONFIG) +COMMON_LIBS = -lchromeos -lbase -lgflags $(LDCONFIG) \ + $(shell $(PKG_CONFIG) --libs glib-2.0 $(LIST_PROXIES_PKGS)) REPORTER_LIBS = $(COMMON_LIBS) -lmetrics +LIST_PROXIES_LIBS = $(COMMON_LIBS) TEST_LIBS = $(COMMON_LIBS) -lgtest -lgmock -INCLUDE_DIRS = -I.. -I$(SYSROOT)/usr/include/google-breakpad +INCLUDE_DIRS = -I.. -I$(SYSROOT)/usr/include/google-breakpad \ + $(shell $(PKG_CONFIG) --cflags glib-2.0 $(LIST_PROXIES_PKGS)) CXXFLAGS += -Wall -Werror @@ -32,6 +38,9 @@ all: $(REPORTER_BINS) $(CRASH_REPORTER): crash_reporter.o $(CRASH_OBJS) $(CXX) $(CXXFLAGS) $^ $(REPORTER_LIBS) -o $@ +$(LIST_PROXIES_BIN): $(LIST_PROXIES_OBJS) + $(CXX) $(CXXFLAGS) $^ $(LIST_PROXIES_LIBS) -o $@ + tests: $(TEST_BINS) %_test: %_test.o $(TEST_OBJS) @@ -41,4 +50,4 @@ tests: $(TEST_BINS) $(CXX) $(CXXFLAGS) $(INCLUDE_DIRS) -c $< -o $@ clean: - rm -rf *.o $(CRASH_BIN) $(TEST_BINS) + rm -rf *.o $(CRASH_BIN) $(LIST_PROXIES_BIN) $(TEST_BINS) diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc new file mode 100644 index 000000000..e895058be --- /dev/null +++ b/crash_reporter/list_proxies.cc @@ -0,0 +1,229 @@ +// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include // for isatty() + +#include +#include + +#include +#include + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/string_tokenizer.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chromeos/dbus/dbus.h" +#include "chromeos/syslog_logging.h" +#include "gflags/gflags.h" + +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +DEFINE_bool(quiet, false, "Only print the proxies"); +// Number of seconds to wait for browser to send us a signal +DEFINE_int32(timeout, 5, "Set timeout for browser resolving proxies"); +DEFINE_bool(verbose, false, "Print additional messages even " + "when not run from a TTY"); +#pragma GCC diagnostic error "-Wstrict-aliasing" + +const char kLibCrosProxyResolveSignalInterface[] = + "org.chromium.CrashReporterLibcrosProxyResolvedInterface"; +const char kLibCrosProxyResolveName[] = "ProxyResolved"; +const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface"; +const char kLibCrosServiceName[] = "org.chromium.LibCrosService"; +const char kLibCrosServicePath[] = "/org/chromium/LibCrosService"; +const char kLibCrosServiceResolveNetworkProxyMethodName[] = + "ResolveNetworkProxy"; +const char kNoProxy[] = "direct://"; + +static const char *GetGErrorMessage(const GError *error) { + if (!error) + return "Unknown error."; + return error->message; +} + +// Copied from src/update_engine/chrome_browser_proxy_resolver.cc +// Parses the browser's answer for resolved proxies. It returns a +// list of strings, each of which is a resolved proxy. +std::deque ParseProxyString(const std::string &input) { + std::deque ret; + // Some of this code taken from + // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and + // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc + StringTokenizer entry_tok(input, ";"); + while (entry_tok.GetNext()) { + std::string token = entry_tok.token(); + TrimWhitespaceASCII(token, TRIM_ALL, &token); + + // Start by finding the first space (if any). + std::string::iterator space; + for (space = token.begin(); space != token.end(); ++space) { + if (IsAsciiWhitespace(*space)) { + break; + } + } + + std::string scheme = std::string(token.begin(), space); + StringToLowerASCII(&scheme); + // Chrome uses "socks" to mean socks4 and "proxy" to mean http. + if (scheme == "socks") + scheme += "4"; + else if (scheme == "proxy") + scheme = "http"; + else if (scheme != "https" && + scheme != "socks4" && + scheme != "socks5" && + scheme != "direct") + continue; // Invalid proxy scheme + + std::string host_and_port = std::string(space, token.end()); + TrimWhitespaceASCII(host_and_port, TRIM_ALL, &host_and_port); + if (scheme != "direct" && host_and_port.empty()) + continue; // Must supply host/port when non-direct proxy used. + ret.push_back(scheme + "://" + host_and_port); + } + if (ret.empty() || *ret.rbegin() != kNoProxy) + ret.push_back(kNoProxy); + return ret; +} + +// Define a signal-watcher class to handle the D-Bus signal sent to us when +// the browser answers our request to resolve proxies. +class BrowserProxyResolvedSignalWatcher : public chromeos::dbus::SignalWatcher { + public: + explicit BrowserProxyResolvedSignalWatcher(GMainLoop *main_loop, + std::deque *proxies) + : main_loop_(main_loop), proxies_(proxies) { } + + virtual void OnSignal(DBusMessage *message) { + // Get args + char *source_url = NULL; + char *proxy_list = NULL; + char *error = NULL; + DBusError arg_error; + dbus_error_init(&arg_error); + if (!dbus_message_get_args(message, &arg_error, + DBUS_TYPE_STRING, &source_url, + DBUS_TYPE_STRING, &proxy_list, + DBUS_TYPE_STRING, &error, + DBUS_TYPE_INVALID)) { + LOG(ERROR) << "Error reading D-Bus signal"; + return; + } + if (!source_url || !proxy_list) { + LOG(ERROR) << "Error getting url, proxy list from D-Bus signal"; + return; + } + + const std::deque &proxies = ParseProxyString(proxy_list); + for (std::deque::const_iterator it = proxies.begin(); + it != proxies.end(); ++it) { + LOG(INFO) << "Found proxy via browser signal: " << (*it).c_str(); + proxies_->push_back(*it); + } + + g_main_loop_quit(main_loop_); + } + + private: + GMainLoop *main_loop_; + std::deque *proxies_; +}; + +static gboolean HandleBrowserTimeout(void *data) { + GMainLoop *main_loop = reinterpret_cast(data); + LOG(ERROR) << "Timeout while waiting for browser to resolve proxy"; + g_main_loop_quit(main_loop); + return false; // only call once +} + +static bool ShowBrowserProxies(const char *url) { + GMainLoop *main_loop = g_main_loop_new(NULL, false); + + chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); + if (!dbus.HasConnection()) { + LOG(ERROR) << "Error connecting to system D-Bus"; + return false; + } + chromeos::dbus::Proxy browser_proxy(dbus, + kLibCrosServiceName, + kLibCrosServicePath, + kLibCrosServiceInterface); + if (!browser_proxy) { + LOG(ERROR) << "Error creating D-Bus proxy to interface " + << "'" << kLibCrosServiceName << "'"; + return false; + } + + // Watch for a proxy-resolved signal sent to us + std::deque proxies; + BrowserProxyResolvedSignalWatcher proxy_resolver(main_loop, &proxies); + proxy_resolver.StartMonitoring(kLibCrosProxyResolveSignalInterface, + kLibCrosProxyResolveName); + + // Request the proxies for our URL. The answer is sent to us via a + // proxy-resolved signal. + GError *gerror = NULL; + if (!dbus_g_proxy_call(browser_proxy.gproxy(), + kLibCrosServiceResolveNetworkProxyMethodName, + &gerror, + G_TYPE_STRING, url, + G_TYPE_STRING, kLibCrosProxyResolveSignalInterface, + G_TYPE_STRING, kLibCrosProxyResolveName, + G_TYPE_INVALID, G_TYPE_INVALID)) { + LOG(ERROR) << "Error performing D-Bus proxy call " + << "'" << kLibCrosServiceResolveNetworkProxyMethodName << "'" + << ": " << GetGErrorMessage(gerror); + return false; + } + + // Setup a timeout in case the browser doesn't respond with our signal + g_timeout_add_seconds(FLAGS_timeout, &HandleBrowserTimeout, main_loop); + + // Loop until we either get the proxy-resolved signal, or until the + // timeout is reached. + g_main_loop_run(main_loop); + + // If there are no proxies, then we failed to get the proxy-resolved + // signal (e.g. timeout was reached). + if (proxies.empty()) + return false; + + for (std::deque::const_iterator it = proxies.begin(); + it != proxies.end(); ++it) { + printf("%s\n", (*it).c_str()); + } + return true; +} + +int main(int argc, char *argv[]) { + google::ParseCommandLineFlags(&argc, &argv, true); + CommandLine::Init(argc, argv); + + // Default to logging to syslog. + int init_flags = chromeos::kLogToSyslog; + // Log to stderr if a TTY (and "-quiet" wasn't passed), or if "-verbose" + // was passed. + if ((!FLAGS_quiet && isatty(STDERR_FILENO)) || FLAGS_verbose) + init_flags |= chromeos::kLogToStderr; + chromeos::InitLog(init_flags); + + ::g_type_init(); + + const char *url = NULL; + if (argc >= 2) { + url = argv[1]; + LOG(INFO) << "Resolving proxies for URL: " << url; + } else { + LOG(INFO) << "Resolving proxies without URL"; + } + + if (!ShowBrowserProxies(url)) { + LOG(ERROR) << "Error resolving proxies via the browser"; + LOG(INFO) << "Assuming direct proxy"; + printf("%s\n", kNoProxy); + } + + return 0; +} From 543eee323d080a57fe54f5f4869b1be65872d329 Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Fri, 15 Apr 2011 21:20:19 -0700 Subject: [PATCH 053/225] crash-reporter: Make use of list_proxies to send crashes via a proxy when appropriate BUG=chromium-os:6828 TEST=Manually ran crash_sender to make sure it would run curl with appropriate --proxy option. Manually ran curl using proxies from "http://proxyconfig.corp.google.com/wpad.dat". Ran a modified crash_sender manually to make sure it passed the appropriate "--proxy" arguments correctly. Review URL: http://codereview.chromium.org/6802003 Change-Id: Ie1414fca1a7dbc265d8aad0eea32a736a5d30d54 --- crash_reporter/crash_sender | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 10fdf37bf..21bd48846 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -68,6 +68,9 @@ RESTRICTED_CERTIFICATES_PATH="/usr/share/chromeos-ca-certificates" # Temp directory for this process. TMP_DIR="" +# Path to list_proxies. +LIST_PROXIES="/usr/bin/list_proxies" + lecho() { logger -t "${TAG}" "$@" } @@ -258,11 +261,18 @@ send_crash() { return 1 fi + # Read in the first proxy, if any, for a given URL. NOTE: The + # double-quotes are necessary due to a bug in dash with the "local" + # builtin command and values that have spaces in them (see + # "https://bugs.launchpad.net/ubuntu/+source/dash/+bug/139097"). + local proxy="`${LIST_PROXIES} -quiet "${url}" | head -1`" + # if a direct connection should be used, unset the proxy variable. + [ "${proxy}" = "direct://" ] && proxy= local report_id="${TMP_DIR}/report_id" local curl_stderr="${TMP_DIR}/curl_stderr" set +e - curl "${url}" \ + curl "${url}" ${proxy:+--proxy "$proxy"} \ --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \ -F "prod=${CHROMEOS_PRODUCT}" \ -F "ver=${chromeos_version}" \ From 3ba6c5b65778964fe3f643ca9df6a17b41181bf2 Mon Sep 17 00:00:00 2001 From: Chris Masone Date: Fri, 13 May 2011 16:57:09 -0700 Subject: [PATCH 054/225] [crash-reporter] rolling to libchrome 87480 BUG=chromium-os:14304 TEST=emerge with features=test Change-Id: Ida7067b3578619bbb99c8b5c626fd6301cdccd8d Reviewed-on: http://gerrit.chromium.org/gerrit/887 Reviewed-by: Chris Masone Tested-by: Chris Masone --- crash_reporter/crash_collector.cc | 5 +++-- crash_reporter/user_collector.cc | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 5247d916c..9f3aed1d1 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -16,6 +16,7 @@ #include "base/eintr_wrapper.h" #include "base/file_util.h" #include "base/logging.h" +#include "base/string_string.h" #include "base/string_util.h" #include "chromeos/process.h" @@ -264,7 +265,7 @@ bool CrashCollector::ReadKeyValueFile( } typedef std::vector StringVector; StringVector lines; - SplitString(contents, '\n', &lines); + base::SplitString(contents, '\n', &lines); bool any_errors = false; for (StringVector::iterator line = lines.begin(); line != lines.end(); ++line) { @@ -275,7 +276,7 @@ bool CrashCollector::ReadKeyValueFile( if (IsCommentLine(*line)) continue; StringVector sides; - SplitString(*line, separator, &sides); + base::SplitString(*line, separator, &sides); if (sides.size() != 2) { any_errors = true; continue; diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index a038e69db..f6ae24d5b 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -15,6 +15,7 @@ #include "base/file_util.h" #include "base/logging.h" +#include "base/string_string.h" #include "base/string_util.h" #include "chromeos/process.h" #include "chromeos/syslog_logging.h" @@ -168,7 +169,7 @@ bool UserCollector::GetIdFromStatus(const char *prefix, // From fs/proc/array.c:task_state(), this file contains: // \nUid:\t\t\t\t\n std::vector status_lines; - SplitString(status_contents, '\n', &status_lines); + base::SplitString(status_contents, '\n', &status_lines); std::vector::iterator line_iterator; for (line_iterator = status_lines.begin(); line_iterator != status_lines.end(); @@ -182,7 +183,7 @@ bool UserCollector::GetIdFromStatus(const char *prefix, std::string id_substring = line_iterator->substr(strlen(prefix), std::string::npos); std::vector ids; - SplitString(id_substring, '\t', &ids); + base::SplitString(id_substring, '\t', &ids); if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) { return false; } From 8a68c7cb30d053de2b3008bee20422258f5e6b3c Mon Sep 17 00:00:00 2001 From: Chris Masone Date: Sat, 14 May 2011 11:44:04 -0700 Subject: [PATCH 055/225] [crash-reporter] bad merge broke the build BUG=chromium-os:14304 TEST=build, features-test Change-Id: I9c2c77c818b9277c506d5186d42677069b1f0704 Reviewed-on: http://gerrit.chromium.org/gerrit/905 Reviewed-by: Chris Masone Tested-by: Chris Masone --- crash_reporter/crash_collector.cc | 2 +- crash_reporter/user_collector.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 9f3aed1d1..e2cd28185 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -16,7 +16,7 @@ #include "base/eintr_wrapper.h" #include "base/file_util.h" #include "base/logging.h" -#include "base/string_string.h" +#include "base/string_split.h" #include "base/string_util.h" #include "chromeos/process.h" diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index f6ae24d5b..5d4a1b1c0 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -15,7 +15,7 @@ #include "base/file_util.h" #include "base/logging.h" -#include "base/string_string.h" +#include "base/string_split.h" #include "base/string_util.h" #include "chromeos/process.h" #include "chromeos/syslog_logging.h" From 538ecbf7d5f4011440c45b27e84902f5953b1f82 Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Wed, 27 Jul 2011 14:13:22 -0700 Subject: [PATCH 056/225] Don't ignore chrome crashes when autotesting. If the file "/mnt/stateful_partition/etc/collect_chrome_crashes" exists on the target, allow chrome crashes to be reported by crash reporter. The intention is that the autotest system will create this file. This is a relatively temporary fix to help track down chrome problems, until the crashes can be handled by crash reporter in the field as well. BUG=chromium-os:17987, chromium-os:17898 TEST=Ran unit tests. Change-Id: I68a584a0b861669117d8e97f5687b4c8fc876011 Reviewed-on: http://gerrit.chromium.org/gerrit/4861 Reviewed-by: Ken Mixter Tested-by: Ken Mixter --- crash_reporter/user_collector.cc | 38 ++++++++++++++++++++++----- crash_reporter/user_collector.h | 8 +++++- crash_reporter/user_collector_test.cc | 17 ++++++++---- 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 5d4a1b1c0..da4265542 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -40,6 +40,8 @@ static const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit"; static const char kCorePipeLimit[] = "4"; static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; static const char kLeaveCoreFile[] = "/root/.leave_core"; +static const char kCollectChromeFile[] = + "/mnt/stateful_partition/etc/collect_chrome_crashes"; static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf"; @@ -398,7 +400,7 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid, exec, minidump_path.value()); - if (!file_util::PathExists(FilePath(kLeaveCoreFile))) { + if (!IsDeveloperImage()) { file_util::Delete(core_path, false); } else { LOG(INFO) << "Leaving core file at " << core_path.value() @@ -416,9 +418,32 @@ bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes, return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name); } +bool UserCollector::IsDeveloperImage() { + // If we're testing crash reporter itself, we don't want to special-case + // for developer images. + if (IsCrashTestInProgress()) + return false; + return file_util::PathExists(FilePath(kLeaveCoreFile)); +} + +bool UserCollector::ShouldIgnoreChromeCrashes() { + // If we're testing crash reporter itself, we don't want to allow an + // override for chrome crashes. And, let's be conservative and only + // allow an override for developer images. + if (!IsCrashTestInProgress() && IsDeveloperImage()) { + // Check if there's an override to indicate we should indeed collect + // chrome crashes. This allows the crashes to still be tracked when + // they occur in autotests. See "crosbug.com/17987". + if (file_util::PathExists(FilePath(kCollectChromeFile))) + return false; + } + // We default to ignoring chrome crashes. + return true; +} + bool UserCollector::ShouldDump(bool has_owner_consent, bool is_developer, - bool is_crash_test_in_progress, + bool ignore_chrome_crashes, const std::string &exec, std::string *reason) { reason->clear(); @@ -426,7 +451,8 @@ bool UserCollector::ShouldDump(bool has_owner_consent, // Treat Chrome crashes as if the user opted-out. We stop counting Chrome // crashes towards user crashes, so user crashes really mean non-Chrome // user-space crashes. - if (exec == "chrome" || exec == "supplied_chrome") { + if ((exec == "chrome" || exec == "supplied_chrome") && + ignore_chrome_crashes) { *reason = "ignoring - chrome crash"; return false; } @@ -434,7 +460,7 @@ bool UserCollector::ShouldDump(bool has_owner_consent, // For developer builds, we always want to keep the crash reports unless // we're testing the crash facilities themselves. This overrides // feedback. Crash sending still obeys consent. - if (is_developer && !is_crash_test_in_progress) { + if (is_developer) { *reason = "developer build - not testing - always dumping"; return true; } @@ -485,8 +511,8 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, std::string reason; bool dump = ShouldDump(is_feedback_allowed_function_(), - file_util::PathExists(FilePath(kLeaveCoreFile)), - IsCrashTestInProgress(), + IsDeveloperImage(), + ShouldIgnoreChromeCrashes(), exec, &reason); diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index a231dd646..55fee8dcf 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -117,9 +117,15 @@ class UserCollector : public CrashCollector { bool ParseCrashAttributes(const std::string &crash_attributes, pid_t *pid, int *signal, std::string *kernel_supplied_name); + + // Returns true if we should consider ourselves to be running on a + // developer image. + bool IsDeveloperImage(); + // Returns true if chrome crashes should be ignored. + bool ShouldIgnoreChromeCrashes(); bool ShouldDump(bool has_owner_consent, bool is_developer, - bool is_crash_test_in_progress, + bool ignore_chrome_crashes, const std::string &exec, std::string *reason); diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 1a5c8c995..b18783fe8 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -124,30 +124,37 @@ TEST_F(UserCollectorTest, ParseCrashAttributes) { TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) { std::string reason; - EXPECT_TRUE(collector_.ShouldDump(false, true, false, + EXPECT_TRUE(collector_.ShouldDump(false, true, true, "chrome-wm", &reason)); EXPECT_EQ("developer build - not testing - always dumping", reason); // When running a crash test, behave as normal. - EXPECT_FALSE(collector_.ShouldDump(false, true, true, + EXPECT_FALSE(collector_.ShouldDump(false, false, true, "chrome-wm", &reason)); EXPECT_EQ("ignoring - no consent", reason); } TEST_F(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage) { std::string reason; - EXPECT_FALSE(collector_.ShouldDump(false, true, false, + // When running a crash test, behave as normal. + EXPECT_FALSE(collector_.ShouldDump(false, false, true, "chrome", &reason)); EXPECT_EQ("ignoring - chrome crash", reason); + + // When in developer mode, test that chrome crashes are not ignored. + EXPECT_TRUE(collector_.ShouldDump(false, true, false, + "chrome", &reason)); + EXPECT_EQ("developer build - not testing - always dumping", + reason); } TEST_F(UserCollectorTest, ShouldDumpUseConsentProductionImage) { std::string result; - EXPECT_FALSE(collector_.ShouldDump(false, false, false, + EXPECT_FALSE(collector_.ShouldDump(false, false, true, "chrome-wm", &result)); EXPECT_EQ("ignoring - no consent", result); - EXPECT_TRUE(collector_.ShouldDump(true, false, false, + EXPECT_TRUE(collector_.ShouldDump(true, false, true, "chrome-wm", &result)); EXPECT_EQ("handling", result); } From 4bd4da26d361dcab858c639dd91773e9a5bf21b9 Mon Sep 17 00:00:00 2001 From: Elly Jones Date: Wed, 3 Aug 2011 11:30:11 -0400 Subject: [PATCH 057/225] crash-reporter: disable CollectBadDirectory Treecloser filed as crosbug.com/18622; temporarily disabling this test to reopen the tree. BUG=chromium-os:18622 TEST=Not anymore Change-Id: I4b7467f6044e02ab1308f449d5aa530a15535933 Signed-off-by: Elly Jones Reviewed-on: http://gerrit.chromium.org/gerrit/5214 Reviewed-by: Chris Masone --- crash_reporter/kernel_collector_test.cc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index bfa2f04d4..431bfca43 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -230,13 +230,14 @@ TEST_F(KernelCollectorTest, CollectNoCrash) { ASSERT_EQ(0, s_crashes); } -TEST_F(KernelCollectorTest, CollectBadDirectory) { - WriteStringToFile(test_kcrash_, "something"); - ASSERT_TRUE(collector_.Collect()); - ASSERT_TRUE(FindLog( - "Unable to create appropriate crash directory")); - ASSERT_EQ(1, s_crashes); -} +// Disabled for crosbug.com/18622 +// TEST_F(KernelCollectorTest, CollectBadDirectory) { +// WriteStringToFile(test_kcrash_, "something"); +// ASSERT_TRUE(collector_.Collect()); +// ASSERT_TRUE(FindLog( +// "Unable to create appropriate crash directory")); +// ASSERT_EQ(1, s_crashes); +// } void KernelCollectorTest::SetUpSuccessfulCollect() { collector_.ForceCrashDirectory(kTestCrashDirectory); From 4fe30db1405498dd21162436eda081486f400fa8 Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Fri, 5 Aug 2011 13:54:52 -0700 Subject: [PATCH 058/225] crash-reporter: Dump crashes outside of cryptohome during autotests. If the file "/mnt/stateful_partition/etc/collect_chrome_crashes" exists on the target, dump crashes to /var/spool/crash/ instead of /home/chronos/user/crash/. This is so they are easily available even after logout. BUG=chromium-os:18637 TEST=Ran unit and remote tests. Change-Id: I340bbb0c1772123192c8bb87872bcda53fae5524 Reviewed-on: http://gerrit.chromium.org/gerrit/5419 Reviewed-by: Ken Mixter Tested-by: Michael Krebs --- crash_reporter/crash_collector.cc | 40 +++++++++++++++++++++++++-- crash_reporter/crash_collector.h | 7 +++++ crash_reporter/user_collector.cc | 32 ++------------------- crash_reporter/user_collector.h | 7 +---- crash_reporter/user_collector_test.cc | 15 +++++----- 5 files changed, 57 insertions(+), 44 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index e2cd28185..b60f7ba69 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -20,12 +20,15 @@ #include "base/string_util.h" #include "chromeos/process.h" +static const char kCollectChromeFile[] = + "/mnt/stateful_partition/etc/collect_chrome_crashes"; +static const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress"; static const char kDefaultUserName[] = "chronos"; +static const char kLeaveCoreFile[] = "/root/.leave_core"; static const char kLsbRelease[] = "/etc/lsb-release"; static const char kShellPath[] = "/bin/sh"; static const char kSystemCrashPath[] = "/var/spool/crash"; static const char kUserCrashPath[] = "/home/chronos/user/crash"; -static const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress"; // Directory mode of the user crash spool directory. static const mode_t kUserCrashPathMode = 0755; @@ -119,7 +122,13 @@ FilePath CrashCollector::GetCrashDirectoryInfo( mode_t *mode, uid_t *directory_owner, gid_t *directory_group) { - if (process_euid == default_user_id) { + // TODO(mkrebs): This can go away once Chrome crashes are handled + // normally (see crosbug.com/5872). + // Check if the user crash directory should be used. If we are + // collecting chrome crashes during autotesting, we want to put them in + // the system crash directory so they are outside the cryptohome -- in + // case we are being run during logout (see crosbug.com/18637). + if (process_euid == default_user_id && IsUserSpecificDirectoryEnabled()) { *mode = kUserCrashPathMode; *directory_owner = default_user_id; *directory_group = default_user_group; @@ -355,3 +364,30 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, bool CrashCollector::IsCrashTestInProgress() { return file_util::PathExists(FilePath(kCrashTestInProgressPath)); } + +bool CrashCollector::IsDeveloperImage() { + // If we're testing crash reporter itself, we don't want to special-case + // for developer images. + if (IsCrashTestInProgress()) + return false; + return file_util::PathExists(FilePath(kLeaveCoreFile)); +} + +bool CrashCollector::ShouldHandleChromeCrashes() { + // If we're testing crash reporter itself, we don't want to allow an + // override for chrome crashes. And, let's be conservative and only + // allow an override for developer images. + if (!IsCrashTestInProgress() && IsDeveloperImage()) { + // Check if there's an override to indicate we should indeed collect + // chrome crashes. This allows the crashes to still be tracked when + // they occur in autotests. See "crosbug.com/17987". + if (file_util::PathExists(FilePath(kCollectChromeFile))) + return true; + } + // We default to ignoring chrome crashes. + return false; +} + +bool CrashCollector::IsUserSpecificDirectoryEnabled() { + return !ShouldHandleChromeCrashes(); +} diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 28503a12b..8759fc8f9 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -133,6 +133,13 @@ class CrashCollector { // Returns true if the a crash test is currently running. bool IsCrashTestInProgress(); + // Returns true if we should consider ourselves to be running on a + // developer image. + bool IsDeveloperImage(); + // Returns true if chrome crashes should be handled. + bool ShouldHandleChromeCrashes(); + // Returns true if user crash directory may be used. + bool IsUserSpecificDirectoryEnabled(); CountCrashFunction count_crash_function_; IsFeedbackAllowedFunction is_feedback_allowed_function_; diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index da4265542..f53a61be8 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -39,9 +39,6 @@ static const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit"; // crashes, but finite to avoid infinitely recursing on crash handling. static const char kCorePipeLimit[] = "4"; static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; -static const char kLeaveCoreFile[] = "/root/.leave_core"; -static const char kCollectChromeFile[] = - "/mnt/stateful_partition/etc/collect_chrome_crashes"; static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf"; @@ -418,32 +415,9 @@ bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes, return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name); } -bool UserCollector::IsDeveloperImage() { - // If we're testing crash reporter itself, we don't want to special-case - // for developer images. - if (IsCrashTestInProgress()) - return false; - return file_util::PathExists(FilePath(kLeaveCoreFile)); -} - -bool UserCollector::ShouldIgnoreChromeCrashes() { - // If we're testing crash reporter itself, we don't want to allow an - // override for chrome crashes. And, let's be conservative and only - // allow an override for developer images. - if (!IsCrashTestInProgress() && IsDeveloperImage()) { - // Check if there's an override to indicate we should indeed collect - // chrome crashes. This allows the crashes to still be tracked when - // they occur in autotests. See "crosbug.com/17987". - if (file_util::PathExists(FilePath(kCollectChromeFile))) - return false; - } - // We default to ignoring chrome crashes. - return true; -} - bool UserCollector::ShouldDump(bool has_owner_consent, bool is_developer, - bool ignore_chrome_crashes, + bool handle_chrome_crashes, const std::string &exec, std::string *reason) { reason->clear(); @@ -452,7 +426,7 @@ bool UserCollector::ShouldDump(bool has_owner_consent, // crashes towards user crashes, so user crashes really mean non-Chrome // user-space crashes. if ((exec == "chrome" || exec == "supplied_chrome") && - ignore_chrome_crashes) { + !handle_chrome_crashes) { *reason = "ignoring - chrome crash"; return false; } @@ -512,7 +486,7 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, std::string reason; bool dump = ShouldDump(is_feedback_allowed_function_(), IsDeveloperImage(), - ShouldIgnoreChromeCrashes(), + ShouldHandleChromeCrashes(), exec, &reason); diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 55fee8dcf..d041628de 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -118,14 +118,9 @@ class UserCollector : public CrashCollector { pid_t *pid, int *signal, std::string *kernel_supplied_name); - // Returns true if we should consider ourselves to be running on a - // developer image. - bool IsDeveloperImage(); - // Returns true if chrome crashes should be ignored. - bool ShouldIgnoreChromeCrashes(); bool ShouldDump(bool has_owner_consent, bool is_developer, - bool ignore_chrome_crashes, + bool handle_chrome_crashes, const std::string &exec, std::string *reason); diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index b18783fe8..7afb712ce 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -124,12 +124,12 @@ TEST_F(UserCollectorTest, ParseCrashAttributes) { TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) { std::string reason; - EXPECT_TRUE(collector_.ShouldDump(false, true, true, + EXPECT_TRUE(collector_.ShouldDump(false, true, false, "chrome-wm", &reason)); EXPECT_EQ("developer build - not testing - always dumping", reason); // When running a crash test, behave as normal. - EXPECT_FALSE(collector_.ShouldDump(false, false, true, + EXPECT_FALSE(collector_.ShouldDump(false, false, false, "chrome-wm", &reason)); EXPECT_EQ("ignoring - no consent", reason); } @@ -137,12 +137,13 @@ TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) { TEST_F(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage) { std::string reason; // When running a crash test, behave as normal. - EXPECT_FALSE(collector_.ShouldDump(false, false, true, + EXPECT_FALSE(collector_.ShouldDump(false, false, false, "chrome", &reason)); EXPECT_EQ("ignoring - chrome crash", reason); - // When in developer mode, test that chrome crashes are not ignored. - EXPECT_TRUE(collector_.ShouldDump(false, true, false, + // When running a developer image, test that chrome crashes are handled + // when the "handle_chrome_crashes" flag is set. + EXPECT_TRUE(collector_.ShouldDump(false, true, true, "chrome", &reason)); EXPECT_EQ("developer build - not testing - always dumping", reason); @@ -150,11 +151,11 @@ TEST_F(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage) { TEST_F(UserCollectorTest, ShouldDumpUseConsentProductionImage) { std::string result; - EXPECT_FALSE(collector_.ShouldDump(false, false, true, + EXPECT_FALSE(collector_.ShouldDump(false, false, false, "chrome-wm", &result)); EXPECT_EQ("ignoring - no consent", result); - EXPECT_TRUE(collector_.ShouldDump(true, false, true, + EXPECT_TRUE(collector_.ShouldDump(true, false, false, "chrome-wm", &result)); EXPECT_EQ("handling", result); } From 1ea8abecd38a71a98c89e556d5a7fc5e9692577e Mon Sep 17 00:00:00 2001 From: Sergiu Iordache Date: Wed, 3 Aug 2011 16:11:36 -0700 Subject: [PATCH 059/225] crash-reporter: Update crash-reporter to work with ramoops The old crash reporter used preserved to get the memory dump. Updated it to use the upstream ramoops module. Also updated the unit tests. Depends on other patches related to this bug report: The backport series starting with: http://gerrit.chromium.org/gerrit/#change,5342 until 5347 (included) The series starting with: http://gerrit.chromium.org/gerrit/#change,5426 until 5429 (included) http://gerrit.chromium.org/gerrit/#change,5159 BUG=chromium-os:12059 TEST=Ran module unit tests, ran logging_KernelCrashServer and platform_KernelErrorPaths, checked manually that the crash file existed. Change-Id: I37bdb30513acfab79997e478af5de35ca33557fd Signed-off-by: Sergiu Iordache Reviewed-on: http://gerrit.chromium.org/gerrit/5432 Reviewed-by: Ken Mixter Reviewed-by: Mandeep Singh Baines Reviewed-by: Michael Krebs --- crash_reporter/kernel_collector.cc | 156 ++++++++++++++++++++---- crash_reporter/kernel_collector.h | 28 ++++- crash_reporter/kernel_collector_test.cc | 58 ++++----- 3 files changed, 181 insertions(+), 61 deletions(-) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 529e35f57..0c69dd608 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -8,15 +8,19 @@ #include "base/logging.h" #include "base/string_util.h" -const char KernelCollector::kClearingSequence[] = " "; static const char kDefaultKernelStackSignature[] = "kernel-UnspecifiedStackSignature"; +static const char kDumpMemSize[] = "/sys/module/ramoops/parameters/mem_size"; +static const char kDumpMemStart[] = + "/sys/module/ramoops/parameters/mem_address"; +static const char kDumpPath[] = "/dev/mem"; +static const char kDumpRecordSize[] = + "/sys/module/ramoops/parameters/record_size"; static const char kKernelExecName[] = "kernel"; const pid_t kKernelPid = 0; static const char kKernelSignatureKey[] = "sig"; // Byte length of maximum human readable portion of a kernel crash signature. static const int kMaxHumanStringLength = 40; -static const char kPreservedDumpPath[] = "/sys/kernel/debug/preserved/kcrash"; const uid_t kRootUid = 0; // Time in seconds from the final kernel log message for a call stack // to count towards the signature of the kcrash. @@ -46,7 +50,10 @@ COMPILE_ASSERT(arraysize(s_pc_regex) == KernelCollector::archCount, KernelCollector::KernelCollector() : is_enabled_(false), - preserved_dump_path_(kPreservedDumpPath) { + ramoops_dump_path_(kDumpPath), + ramoops_record_size_path_(kDumpRecordSize), + ramoops_dump_start_path_(kDumpMemStart), + ramoops_dump_size_path_(kDumpMemSize) { // We expect crash dumps in the format of the architecture we are built for. arch_ = GetCompilerArch(); } @@ -55,16 +62,130 @@ KernelCollector::~KernelCollector() { } void KernelCollector::OverridePreservedDumpPath(const FilePath &file_path) { - preserved_dump_path_ = file_path; + ramoops_dump_path_ = file_path; +} + +bool KernelCollector::ReadRecordToString(std::string *contents, + unsigned int current_record, + bool *record_found) { + FILE *file = fopen(ramoops_dump_path_.value().c_str(), "rb"); + + // A record is a ramoops dump. It has an associated size of "record_size". + std::string record; + std::string captured; + static const unsigned int s_total_size_to_read = 4096; + size_t len = s_total_size_to_read; + + // Ramoops appends a header to a crash which contains ==== followed by a + // timestamp. Ignore the header. + pcrecpp::RE record_re("====\\d+\\.\\d+\n(.*)", + pcrecpp::RE_Options().set_multiline(true).set_dotall(true)); + + if (!file) { + LOG(ERROR) << "Unable to open " << kDumpPath; + return false; + } + // We're reading from /dev/mem, so we have to fseek to the desired area. + fseek(file, mem_start_ + current_record * record_size_, SEEK_SET); + size_t size_to_read = record_size_; + + while (size_to_read > 0 && !feof(file) && len == s_total_size_to_read) { + char buf[s_total_size_to_read + 1]; + len = fread(buf, 1, s_total_size_to_read, file); + // In the rare case that we read less than the minimum size null terminate + // the string properly + if (len < s_total_size_to_read) { + buf[len] = 0; + } else { + buf[s_total_size_to_read] = 0; + } + // Add the data.. if the string is null terminated and smaller then the + // buffer than add just that part. Otherwise add the whole buffer. + record.append(buf); + size_to_read -= len; + } + + fclose(file); + + if (record_re.FullMatch(record, &captured)){ + // Found a match, append it to the content. + contents->append(captured); + *record_found = true; + } else { + *record_found = false; + } + + return true; +} + +bool LoadValue(FilePath path, unsigned int *element){ + std::string buf; + char *end; + if (!file_util::ReadFileToString(path, &buf)) { + LOG(ERROR) << "Unable to read " << path.value(); + return false; + } + *element = strtol(buf.c_str(), &end, 10); + if (end != NULL && *end =='\0') { + LOG(ERROR) << "Invalid number in " << path.value(); + return false; + } + + return true; +} + +bool KernelCollector::LoadParameters() { + // Read the parameters from the /sys/module/ramoops/parameters/* files + // and convert them to numbers. + + if (!LoadValue(ramoops_record_size_path_, &record_size_)) + return false; + if (record_size_ <= 0){ + LOG(ERROR) << "Record size is <= 0" ; + } + if (!LoadValue(ramoops_dump_start_path_, &mem_start_)) + return false; + if (!LoadValue(ramoops_dump_size_path_, &mem_size_)) + return false; + if (mem_size_ <= 0){ + LOG(ERROR) << "Memory size is <= 0" ; + } + + return true; +} + +void KernelCollector::SetParameters(size_t record_size, size_t mem_start, + size_t mem_size) { + // Used for unit testing. + record_size_ = record_size; + mem_start_ = mem_start; + mem_size_ = mem_size; } bool KernelCollector::LoadPreservedDump(std::string *contents) { + // Load dumps from the preserved memory and save them in contents. + // Since the system is set to restart on oops we won't actually ever have + // multiple records (only 0 or 1), but check in case we don't restart on + // oops in the future. + bool any_records_found = false; + bool record_found = false; // clear contents since ReadFileToString actually appends to the string. contents->clear(); - if (!file_util::ReadFileToString(preserved_dump_path_, contents)) { - LOG(ERROR) << "Unable to read " << preserved_dump_path_.value(); + + for (size_t i = 0; i < mem_size_ / record_size_; ++i) { + if (!ReadRecordToString(contents, i, &record_found)) { + break; + } + if (record_found) { + any_records_found = true; + } + } + + if (!any_records_found) { + LOG(ERROR) << "No valid records found in " << ramoops_dump_path_.value(); return false; } + return true; } @@ -149,7 +270,7 @@ bool KernelCollector::Enable() { LOG(WARNING) << "KernelCollector does not understand this architecture"; return false; } - else if (!file_util::PathExists(preserved_dump_path_)) { + else if (!file_util::PathExists(ramoops_dump_path_)) { LOG(WARNING) << "Kernel does not support crash dumping"; return false; } @@ -161,20 +282,6 @@ bool KernelCollector::Enable() { return true; } -bool KernelCollector::ClearPreservedDump() { - // It is necessary to write at least one byte to the kcrash file for - // the log to actually be cleared. - if (file_util::WriteFile( - preserved_dump_path_, - kClearingSequence, - strlen(kClearingSequence)) != strlen(kClearingSequence)) { - LOG(ERROR) << "Failed to clear kernel crash dump"; - return false; - } - LOG(INFO) << "Cleared kernel crash diagnostics"; - return true; -} - // Hash a string to a number. We define our own hash function to not // be dependent on a C++ library that might change. This function // uses basically the same approach as tr1/functional_hash.h but with @@ -372,6 +479,10 @@ bool KernelCollector::ComputeKernelStackSignature( bool KernelCollector::Collect() { std::string kernel_dump; FilePath root_crash_directory; + + if (!LoadParameters()) { + return false; + } if (!LoadPreservedDump(&kernel_dump)) { return false; } @@ -427,9 +538,6 @@ bool KernelCollector::Collect() { LOG(INFO) << "Stored kcrash to " << kernel_crash_path.value(); } - if (!ClearPreservedDump()) { - return false; - } return true; } diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index 1c86f40c5..4dfb1d7dd 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -54,9 +54,11 @@ class KernelCollector : public CrashCollector { void SetArch(enum ArchKind arch); enum ArchKind GetArch() { return arch_; } + // Set the parameters for reading the crash dump. + void SetParameters(size_t record_size, size_t mem_start, size_t mem_size); + private: friend class KernelCollectorTest; - FRIEND_TEST(KernelCollectorTest, ClearPreservedDump); FRIEND_TEST(KernelCollectorTest, LoadPreservedDump); FRIEND_TEST(KernelCollectorTest, StripSensitiveDataBasic); FRIEND_TEST(KernelCollectorTest, StripSensitiveDataBulk); @@ -64,9 +66,22 @@ class KernelCollector : public CrashCollector { FRIEND_TEST(KernelCollectorTest, CollectOK); bool LoadPreservedDump(std::string *contents); - bool ClearPreservedDump(); void StripSensitiveData(std::string *kernel_dump); + virtual bool LoadParameters(); + bool HasMoreRecords(); + + // Read a record to string, modified from file_utils since that didn't + // provide a way to restrict the read length. + // Return value indicates (only) error state: + // * false when we get an error (can't read from dump location). + // * true if no error occured. + // Not finding a valid record is not an error state and is signaled by the + // record_found output parameter. + bool ReadRecordToString(std::string *contents, + unsigned int current_record, + bool *record_found); + void ProcessStackTrace(pcrecpp::StringPiece kernel_dump, bool print_diagnostics, unsigned *hash, @@ -83,8 +98,13 @@ class KernelCollector : public CrashCollector { enum ArchKind GetCompilerArch(void); bool is_enabled_; - FilePath preserved_dump_path_; - static const char kClearingSequence[]; + size_t mem_start_; + size_t mem_size_; + FilePath ramoops_dump_path_; + FilePath ramoops_record_size_path_; + FilePath ramoops_dump_start_path_; + FilePath ramoops_dump_size_path_; + size_t record_size_; // The architecture of kernel dump strings we are working with. enum ArchKind arch_; diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 431bfca43..35bb0ec33 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -28,6 +28,14 @@ bool IsMetrics() { return s_metrics; } +class TKernelCollector : public KernelCollector { + bool LoadParameters() { + // Since we don't have /sys/module/ramoops/parameters on our system just + // return true instead of getting the parameters from the files. + return true; + } +}; + class KernelCollectorTest : public ::testing::Test { void SetUp() { s_crashes = 0; @@ -49,26 +57,28 @@ class KernelCollectorTest : public ::testing::Test { } void SetUpSuccessfulCollect(); - void CheckPreservedDumpClear(); void ComputeKernelStackSignatureCommon(); - KernelCollector collector_; + TKernelCollector collector_; FilePath test_kcrash_; }; TEST_F(KernelCollectorTest, ComputeKernelStackSignatureBase) { // Make sure the normal build architecture is detected - EXPECT_TRUE(collector_.GetArch() != KernelCollector::archUnknown); + EXPECT_TRUE(collector_.GetArch() != TKernelCollector::archUnknown); } TEST_F(KernelCollectorTest, LoadPreservedDump) { ASSERT_FALSE(file_util::PathExists(test_kcrash_)); std::string dump; + dump.clear(); + + collector_.SetParameters(9, 0, 9); + WriteStringToFile(test_kcrash_, "emptydata"); ASSERT_FALSE(collector_.LoadPreservedDump(&dump)); - WriteStringToFile(test_kcrash_, ""); - ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); ASSERT_EQ("", dump); - WriteStringToFile(test_kcrash_, "something"); + collector_.SetParameters(17, 0, 17); + WriteStringToFile(test_kcrash_, "====1.1\nsomething"); ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); ASSERT_EQ("something", dump); } @@ -89,17 +99,6 @@ TEST_F(KernelCollectorTest, EnableOK) { ASSERT_EQ(s_crashes, 0); } -TEST_F(KernelCollectorTest, ClearPreservedDump) { - std::string dump; - ASSERT_FALSE(file_util::PathExists(test_kcrash_)); - WriteStringToFile(test_kcrash_, "something"); - ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); - ASSERT_EQ("something", dump); - ASSERT_TRUE(collector_.ClearPreservedDump()); - ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); - ASSERT_EQ(KernelCollector::kClearingSequence, dump); -} - TEST_F(KernelCollectorTest, StripSensitiveDataBasic) { // Basic tests of StripSensitiveData... @@ -219,7 +218,8 @@ TEST_F(KernelCollectorTest, StripSensitiveDataSample) { TEST_F(KernelCollectorTest, CollectPreservedFileMissing) { ASSERT_FALSE(collector_.Collect()); - ASSERT_TRUE(FindLog("Unable to read test/kcrash")); + ASSERT_TRUE(FindLog("Unable to open")); + ASSERT_TRUE(FindLog("No valid records found")); ASSERT_EQ(0, s_crashes); } @@ -231,35 +231,28 @@ TEST_F(KernelCollectorTest, CollectNoCrash) { } // Disabled for crosbug.com/18622 -// TEST_F(KernelCollectorTest, CollectBadDirectory) { -// WriteStringToFile(test_kcrash_, "something"); +//TEST_F(KernelCollectorTest, CollectBadDirectory) { +// collector_.SetParameters(17, 0, 17); +// WriteStringToFile(test_kcrash_, "====1.1\nsomething"); // ASSERT_TRUE(collector_.Collect()); // ASSERT_TRUE(FindLog( // "Unable to create appropriate crash directory")); // ASSERT_EQ(1, s_crashes); -// } +//} void KernelCollectorTest::SetUpSuccessfulCollect() { collector_.ForceCrashDirectory(kTestCrashDirectory); - WriteStringToFile(test_kcrash_, "something"); + collector_.SetParameters(17, 0, 17); + WriteStringToFile(test_kcrash_, "====1.1\nsomething"); ASSERT_EQ(0, s_crashes); } -void KernelCollectorTest::CheckPreservedDumpClear() { - // Make sure the preserved dump is now clear. - std::string dump; - ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); - ASSERT_EQ(KernelCollector::kClearingSequence, dump); -} - TEST_F(KernelCollectorTest, CollectOptedOut) { SetUpSuccessfulCollect(); s_metrics = false; ASSERT_TRUE(collector_.Collect()); ASSERT_TRUE(FindLog("(ignoring - no consent)")); ASSERT_EQ(0, s_crashes); - - CheckPreservedDumpClear(); } TEST_F(KernelCollectorTest, CollectOK) { @@ -283,7 +276,6 @@ TEST_F(KernelCollectorTest, CollectOK) { ASSERT_TRUE(file_util::ReadFileToString(FilePath(filename), &contents)); ASSERT_EQ("something", contents); - CheckPreservedDumpClear(); } // Perform tests which are common across architectures @@ -362,7 +354,7 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignatureARM) { "[] (proc_reg_write+0x88/0x9c)\n"; std::string signature; - collector_.SetArch(KernelCollector::archArm); + collector_.SetArch(TKernelCollector::archArm); EXPECT_TRUE( collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false)); EXPECT_EQ("kernel-write_breakme-97D3E92F", signature); From e2c1b179047c478a413eb13269093aef3e97829f Mon Sep 17 00:00:00 2001 From: Katie Roberts-Hoffman Date: Tue, 20 Sep 2011 09:53:02 -0700 Subject: [PATCH 060/225] Add HWID information for ARM via crossystem Added HWID query via crossystem if not available via ACPI. This populates the hwclass field for crash reports. BUG=None TEST=Manually confirmed ARM platform crash reports contains correct hwclass and manually confirmed ZGB report contains previously reported hwclas Change-Id: I1216ed13c0b04d829313103d3bb736f5227a772b Reviewed-on: http://gerrit.chromium.org/gerrit/7982 Reviewed-by: Doug Anderson Reviewed-by: Micah Catlin Commit-Ready: Katie Roberts-Hoffman Tested-by: Katie Roberts-Hoffman --- crash_reporter/crash_sender | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 21bd48846..a7a2fc46e 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -196,6 +196,8 @@ get_board() { get_hardware_class() { if [ -r "${HWCLASS_PATH}" ]; then cat "${HWCLASS_PATH}" + elif crossystem hwid > /dev/null 2>&1; then + echo "$(crossystem hwid)" else echo "undefined" fi From 65b4c1efb0bca77f3a73401499adc5c39d982c18 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 21 Sep 2011 12:41:29 -0400 Subject: [PATCH 061/225] touch up 64bit build cleanliness Some of the types being used happened to work on 32bit systems because size_t is pretty much an unsigned int. But with a 64bit system, we see: kernel_collector.cc: In member function 'virtual bool KernelCollector::LoadParameters()': kernel_collector.cc:141: error: cannot convert 'size_t*' to 'unsigned int*' for argument '2' to 'bool LoadValue(FilePath, unsigned int*)' This is easy to fix by using the correct type in LoadValue(). Next up, we get a failure due to the assumption that int64 can be displayed using the lld format string. But on 64bit systems, this doesn't work: crash_collector.cc: In member function 'void CrashCollector::WriteCrashMetaData(const FilePath&, const std::string&, const std::string&)': crash_collector.cc:355: error: format '%lld' expects type 'long long int', but argument 6 has type 'int64' This takes a little bit more magic to make work. Since printf itself does not provide a printf string to handle "64bit" types, use the macros that POSIX provides in inttypes.h. The printf string is a little uglier, but now should work for all targets. BUG=chromium-os:20636 TEST=`emerge-amd64-generic crash-reporter` now works TEST=`emerge-x86-generic crash-reporter` still works TEST=build+booting x86-alex still works Change-Id: I8401f2ad932223085dfbe54541590e9b65297783 Reviewed-on: http://gerrit.chromium.org/gerrit/8051 Reviewed-by: Ken Mixter Tested-by: Mike Frysinger Commit-Ready: Mike Frysinger Reviewed-by: Michael Krebs --- crash_reporter/crash_collector.cc | 4 +++- crash_reporter/kernel_collector.cc | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index b60f7ba69..fc7a52cdd 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -10,6 +10,8 @@ #include // for mode_t. #include // For waitpid. #include // For execv and fork. +#define __STDC_FORMAT_MACROS // PRId64 +#include #include @@ -346,7 +348,7 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, std::string meta_data = StringPrintf("%sexec_name=%s\n" "ver=%s\n" "payload=%s\n" - "payload_size=%lld\n" + "payload_size=%"PRId64"\n" "done=1\n", extra_metadata_.c_str(), exec_name.c_str(), diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 0c69dd608..8652d35d4 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -118,7 +118,7 @@ bool KernelCollector::ReadRecordToString(std::string *contents, return true; } -bool LoadValue(FilePath path, unsigned int *element){ +bool LoadValue(FilePath path, size_t *element){ std::string buf; char *end; if (!file_util::ReadFileToString(path, &buf)) { From ae33866aeaa2071fd736abc2a7dc27c3fe9e8e51 Mon Sep 17 00:00:00 2001 From: Sergiu Iordache Date: Fri, 5 Aug 2011 15:32:15 -0700 Subject: [PATCH 062/225] crash-reporter: Activate updated BadDirectory test Activated the BadDirectory test which was breaking the build. BUG=chromium-os:18622 TEST=Ran the unit tests locally and it succeded. Change-Id: I439d9fb40ec37261ef839c82817d6bc67e683c7d Signed-off-by: Sergiu Iordache Reviewed-on: http://gerrit.chromium.org/gerrit/5433 Reviewed-by: Ken Mixter Tested-by: Michael Krebs --- crash_reporter/kernel_collector_test.cc | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 35bb0ec33..87c251b24 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -230,15 +230,14 @@ TEST_F(KernelCollectorTest, CollectNoCrash) { ASSERT_EQ(0, s_crashes); } -// Disabled for crosbug.com/18622 -//TEST_F(KernelCollectorTest, CollectBadDirectory) { -// collector_.SetParameters(17, 0, 17); -// WriteStringToFile(test_kcrash_, "====1.1\nsomething"); -// ASSERT_TRUE(collector_.Collect()); -// ASSERT_TRUE(FindLog( -// "Unable to create appropriate crash directory")); -// ASSERT_EQ(1, s_crashes); -//} +TEST_F(KernelCollectorTest, CollectBadDirectory) { + collector_.SetParameters(17, 0, 17); + WriteStringToFile(test_kcrash_, "====1.1\nsomething"); + ASSERT_TRUE(collector_.Collect()); + ASSERT_TRUE(FindLog( + "Unable to create appropriate crash directory")); + ASSERT_EQ(1, s_crashes); +} void KernelCollectorTest::SetUpSuccessfulCollect() { collector_.ForceCrashDirectory(kTestCrashDirectory); From b1ac89bb1f53cba2b9018a47826167a2375a246b Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Mon, 26 Sep 2011 20:04:04 -0700 Subject: [PATCH 063/225] Revert "crash-reporter: Activate updated BadDirectory test" This reverts commit 4a029228b93edcd9cf79c2578acd27eb56be5bce Although this passes in my local checkout, it broke the buildbots. Will investigate more. Change-Id: Ib6e33bb916b43b1a42f8f8e4c0c69eb360c3181c Reviewed-on: http://gerrit.chromium.org/gerrit/8332 Reviewed-by: Michael Krebs Tested-by: Michael Krebs --- crash_reporter/kernel_collector_test.cc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 87c251b24..35bb0ec33 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -230,14 +230,15 @@ TEST_F(KernelCollectorTest, CollectNoCrash) { ASSERT_EQ(0, s_crashes); } -TEST_F(KernelCollectorTest, CollectBadDirectory) { - collector_.SetParameters(17, 0, 17); - WriteStringToFile(test_kcrash_, "====1.1\nsomething"); - ASSERT_TRUE(collector_.Collect()); - ASSERT_TRUE(FindLog( - "Unable to create appropriate crash directory")); - ASSERT_EQ(1, s_crashes); -} +// Disabled for crosbug.com/18622 +//TEST_F(KernelCollectorTest, CollectBadDirectory) { +// collector_.SetParameters(17, 0, 17); +// WriteStringToFile(test_kcrash_, "====1.1\nsomething"); +// ASSERT_TRUE(collector_.Collect()); +// ASSERT_TRUE(FindLog( +// "Unable to create appropriate crash directory")); +// ASSERT_EQ(1, s_crashes); +//} void KernelCollectorTest::SetUpSuccessfulCollect() { collector_.ForceCrashDirectory(kTestCrashDirectory); From ce9556e1c490113bf66eaf5bb3f2d9241e209474 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 4 Nov 2011 20:49:09 +0000 Subject: [PATCH 064/225] Switch away from /dev/mem to new ramoops pstore interface Use the new ramoops pstore interface instead of reading /dev/mem or needing to know anything about the ramoops memory configurations. BUG=chromium-os:12059 TEST=x86-alex build and boot, logging_KernelCrashServer passes, local "make tests" passes. Change-Id: Ie713d4f93df7f666537c7181b1b438d7b85d4861 Signed-off-by: Kees Cook Reviewed-on: https://gerrit.chromium.org/gerrit/11245 Reviewed-by: Will Drewry --- crash_reporter/kernel_collector.cc | 109 +++++++----------------- crash_reporter/kernel_collector.h | 13 +-- crash_reporter/kernel_collector_test.cc | 9 +- 3 files changed, 38 insertions(+), 93 deletions(-) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 8652d35d4..de4f92db7 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -10,13 +10,11 @@ static const char kDefaultKernelStackSignature[] = "kernel-UnspecifiedStackSignature"; -static const char kDumpMemSize[] = "/sys/module/ramoops/parameters/mem_size"; -static const char kDumpMemStart[] = - "/sys/module/ramoops/parameters/mem_address"; -static const char kDumpPath[] = "/dev/mem"; -static const char kDumpRecordSize[] = - "/sys/module/ramoops/parameters/record_size"; +static const char kDumpPath[] = "/dev/pstore"; +static const char kDumpFormat[] = "dmesg-ramoops-%d"; static const char kKernelExecName[] = "kernel"; +// Maximum number of records to examine in the kDumpPath. +static const size_t kMaxDumpRecords = 100; const pid_t kKernelPid = 0; static const char kKernelSignatureKey[] = "sig"; // Byte length of maximum human readable portion of a kernel crash signature. @@ -50,10 +48,7 @@ COMPILE_ASSERT(arraysize(s_pc_regex) == KernelCollector::archCount, KernelCollector::KernelCollector() : is_enabled_(false), - ramoops_dump_path_(kDumpPath), - ramoops_record_size_path_(kDumpRecordSize), - ramoops_dump_start_path_(kDumpMemStart), - ramoops_dump_size_path_(kDumpMemSize) { + ramoops_dump_path_(kDumpPath) { // We expect crash dumps in the format of the architecture we are built for. arch_ = GetCompilerArch(); } @@ -66,46 +61,23 @@ void KernelCollector::OverridePreservedDumpPath(const FilePath &file_path) { } bool KernelCollector::ReadRecordToString(std::string *contents, - unsigned int current_record, + size_t current_record, bool *record_found) { - FILE *file = fopen(ramoops_dump_path_.value().c_str(), "rb"); - // A record is a ramoops dump. It has an associated size of "record_size". std::string record; std::string captured; - static const unsigned int s_total_size_to_read = 4096; - size_t len = s_total_size_to_read; // Ramoops appends a header to a crash which contains ==== followed by a // timestamp. Ignore the header. pcrecpp::RE record_re("====\\d+\\.\\d+\n(.*)", pcrecpp::RE_Options().set_multiline(true).set_dotall(true)); - if (!file) { - LOG(ERROR) << "Unable to open " << kDumpPath; + FilePath ramoops_record; + GetRamoopsRecordPath(&ramoops_record, current_record); + if (!file_util::ReadFileToString(ramoops_record, &record)) { + LOG(ERROR) << "Unable to open " << ramoops_record.value(); return false; } - // We're reading from /dev/mem, so we have to fseek to the desired area. - fseek(file, mem_start_ + current_record * record_size_, SEEK_SET); - size_t size_to_read = record_size_; - - while (size_to_read > 0 && !feof(file) && len == s_total_size_to_read) { - char buf[s_total_size_to_read + 1]; - len = fread(buf, 1, s_total_size_to_read, file); - // In the rare case that we read less than the minimum size null terminate - // the string properly - if (len < s_total_size_to_read) { - buf[len] = 0; - } else { - buf[s_total_size_to_read] = 0; - } - // Add the data.. if the string is null terminated and smaller then the - // buffer than add just that part. Otherwise add the whole buffer. - record.append(buf); - size_to_read -= len; - } - - fclose(file); if (record_re.FullMatch(record, &captured)){ // Found a match, append it to the content. @@ -118,48 +90,25 @@ bool KernelCollector::ReadRecordToString(std::string *contents, return true; } -bool LoadValue(FilePath path, size_t *element){ - std::string buf; - char *end; - if (!file_util::ReadFileToString(path, &buf)) { - LOG(ERROR) << "Unable to read " << path.value(); - return false; - } - *element = strtol(buf.c_str(), &end, 10); - if (end != NULL && *end =='\0') { - LOG(ERROR) << "Invalid number in " << path.value(); - return false; - } - - return true; +void KernelCollector::GetRamoopsRecordPath(FilePath *path, + size_t record) { + *path = ramoops_dump_path_.Append(StringPrintf(kDumpFormat, record)); } bool KernelCollector::LoadParameters() { - // Read the parameters from the /sys/module/ramoops/parameters/* files - // and convert them to numbers. + // Discover how many ramoops records are being exported by the driver. + size_t count; - if (!LoadValue(ramoops_record_size_path_, &record_size_)) - return false; - if (record_size_ <= 0){ - LOG(ERROR) << "Record size is <= 0" ; - } - if (!LoadValue(ramoops_dump_start_path_, &mem_start_)) - return false; - if (!LoadValue(ramoops_dump_size_path_, &mem_size_)) - return false; - if (mem_size_ <= 0){ - LOG(ERROR) << "Memory size is <= 0" ; + for (count = 0; count < kMaxDumpRecords; ++count) { + FilePath ramoops_record; + GetRamoopsRecordPath(&ramoops_record, count); + + if (!file_util::PathExists(ramoops_record)) + break; } - return true; -} - -void KernelCollector::SetParameters(size_t record_size, size_t mem_start, - size_t mem_size) { - // Used for unit testing. - record_size_ = record_size; - mem_start_ = mem_start; - mem_size_ = mem_size; + records_ = count; + return (records_ > 0); } bool KernelCollector::LoadPreservedDump(std::string *contents) { @@ -172,7 +121,7 @@ bool KernelCollector::LoadPreservedDump(std::string *contents) { // clear contents since ReadFileToString actually appends to the string. contents->clear(); - for (size_t i = 0; i < mem_size_ / record_size_; ++i) { + for (size_t i = 0; i < records_; ++i) { if (!ReadRecordToString(contents, i, &record_found)) { break; } @@ -270,9 +219,13 @@ bool KernelCollector::Enable() { LOG(WARNING) << "KernelCollector does not understand this architecture"; return false; } - else if (!file_util::PathExists(ramoops_dump_path_)) { - LOG(WARNING) << "Kernel does not support crash dumping"; - return false; + else { + FilePath ramoops_record; + GetRamoopsRecordPath(&ramoops_record, 0); + if (!file_util::PathExists(ramoops_record)) { + LOG(WARNING) << "Kernel does not support crash dumping"; + return false; + } } // To enable crashes, we will eventually need to set diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index 4dfb1d7dd..e8698a7aa 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -54,9 +54,6 @@ class KernelCollector : public CrashCollector { void SetArch(enum ArchKind arch); enum ArchKind GetArch() { return arch_; } - // Set the parameters for reading the crash dump. - void SetParameters(size_t record_size, size_t mem_start, size_t mem_size); - private: friend class KernelCollectorTest; FRIEND_TEST(KernelCollectorTest, LoadPreservedDump); @@ -68,6 +65,7 @@ class KernelCollector : public CrashCollector { bool LoadPreservedDump(std::string *contents); void StripSensitiveData(std::string *kernel_dump); + void GetRamoopsRecordPath(FilePath *path, size_t record); virtual bool LoadParameters(); bool HasMoreRecords(); @@ -79,7 +77,7 @@ class KernelCollector : public CrashCollector { // Not finding a valid record is not an error state and is signaled by the // record_found output parameter. bool ReadRecordToString(std::string *contents, - unsigned int current_record, + size_t current_record, bool *record_found); void ProcessStackTrace(pcrecpp::StringPiece kernel_dump, @@ -98,13 +96,8 @@ class KernelCollector : public CrashCollector { enum ArchKind GetCompilerArch(void); bool is_enabled_; - size_t mem_start_; - size_t mem_size_; FilePath ramoops_dump_path_; - FilePath ramoops_record_size_path_; - FilePath ramoops_dump_start_path_; - FilePath ramoops_dump_size_path_; - size_t record_size_; + size_t records_; // The architecture of kernel dump strings we are working with. enum ArchKind arch_; diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 35bb0ec33..10e9e3604 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -43,9 +43,11 @@ class KernelCollectorTest : public ::testing::Test { collector_.Initialize(CountCrash, IsMetrics); mkdir("test", 0777); + mkdir(kTestKCrash, 0777); test_kcrash_ = FilePath(kTestKCrash); collector_.OverridePreservedDumpPath(test_kcrash_); - unlink(kTestKCrash); + test_kcrash_ = test_kcrash_.Append("dmesg-ramoops-0"); + unlink(test_kcrash_.value().c_str()); mkdir(kTestCrashDirectory, 0777); chromeos::ClearLog(); } @@ -73,11 +75,10 @@ TEST_F(KernelCollectorTest, LoadPreservedDump) { std::string dump; dump.clear(); - collector_.SetParameters(9, 0, 9); WriteStringToFile(test_kcrash_, "emptydata"); ASSERT_FALSE(collector_.LoadPreservedDump(&dump)); ASSERT_EQ("", dump); - collector_.SetParameters(17, 0, 17); + WriteStringToFile(test_kcrash_, "====1.1\nsomething"); ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); ASSERT_EQ("something", dump); @@ -232,7 +233,6 @@ TEST_F(KernelCollectorTest, CollectNoCrash) { // Disabled for crosbug.com/18622 //TEST_F(KernelCollectorTest, CollectBadDirectory) { -// collector_.SetParameters(17, 0, 17); // WriteStringToFile(test_kcrash_, "====1.1\nsomething"); // ASSERT_TRUE(collector_.Collect()); // ASSERT_TRUE(FindLog( @@ -242,7 +242,6 @@ TEST_F(KernelCollectorTest, CollectNoCrash) { void KernelCollectorTest::SetUpSuccessfulCollect() { collector_.ForceCrashDirectory(kTestCrashDirectory); - collector_.SetParameters(17, 0, 17); WriteStringToFile(test_kcrash_, "====1.1\nsomething"); ASSERT_EQ(0, s_crashes); } From 9ee1f5f034e44dd20b3cb229db601eeb1098a3d6 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Tue, 25 Oct 2011 02:15:05 +0000 Subject: [PATCH 065/225] crash-reporter: always write kernel crashes for dev builds Added check for devloper build. Always write kernel crash files when running devloper builds. BUG=None TEST=run "echo bug > /proc/breakme" and ensure the kcrash files get created under /var/spool/crash Change-Id: Iced35df9db1f997c41138f082ee50a3506a88eb2 Reviewed-on: https://gerrit.chromium.org/gerrit/10615 Reviewed-by: Sameer Nanda Tested-by: Sameer Nanda --- crash_reporter/kernel_collector.cc | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index de4f92db7..bdd1dd203 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -448,11 +448,18 @@ bool KernelCollector::Collect() { signature = kDefaultKernelStackSignature; } - bool feedback = is_feedback_allowed_function_(); + std::string reason = "handling"; + bool feedback = true; + if (IsDeveloperImage()) { + reason = "developer build - always dumping"; + feedback = true; + } else if (!is_feedback_allowed_function_()) { + reason = "ignoring - no consent"; + feedback = false; + } LOG(INFO) << "Received prior crash notification from " - << "kernel (signature " << signature << ") (" - << (feedback ? "handling" : "ignoring - no consent") << ")"; + << "kernel (signature " << signature << ") (" << reason << ")"; if (feedback) { count_crash_function_(); From 294d5d1eeed1634800e0a52fe1bcf7418ac5d958 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Wed, 4 Jan 2012 20:40:15 -0800 Subject: [PATCH 066/225] crash-reporter: Change layout for the container directory of procfs files. This CL modifies UserCollector to copy procfs files of a crashed process into /tmp/crash_reporter/ instead of /tmp/crash_reporter.. The new directory layout makes it easier for autotest to archive leftover files under /tmp/crash_reporter due to core2md failures. BUG=chromium-os:24646 TEST=Tested the following: 1. Build crash-reporter and run unit tests. 2. Run the following autotest tests on a Cr48: - logging_CrashSender - logging_UserCrash Change-Id: I435374eabc1574216881318ddacdfa9d1648c875 Reviewed-on: https://gerrit.chromium.org/gerrit/13697 Commit-Ready: Ben Chan Reviewed-by: Ben Chan Tested-by: Ben Chan --- crash_reporter/user_collector.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index f53a61be8..01af4eb0a 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -366,10 +366,9 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid, return false; } - // Directory like /tmp/crash_reporter.1234 which contains the + // Directory like /tmp/crash_reporter/1234 which contains the // procfs entries and other temporary files used during conversion. - FilePath container_dir = FilePath("/tmp").Append( - StringPrintf("crash_reporter.%d", pid)); + FilePath container_dir(StringPrintf("/tmp/crash_reporter/%d", pid)); // Delete a pre-existing directory from crash reporter that may have // been left around for diagnostics from a failed conversion attempt. // If we don't, existing files can cause forking to fail. From f640099ceb6e945136110a6d41d3af66786a5d0d Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Thu, 29 Dec 2011 13:18:35 -0800 Subject: [PATCH 067/225] Use hung thread stack for watchdog panics. With this change the crash reporter tries to determine if the last stack dump in the log file is from the hung thread watchdog, and in that case creates a crash signature that's based on the previous stack dump. When the previous stack is used because the watchdog function was detected, prepend "(HANG)" to the function name or kernel message in the signature. Also use an earlier stack signature if all functions in the last one are uncertain. This happens in many cases (about 1/2). It's likely a bug in the kernel but until we fix it this workaround will be helpful. I tested this manually with the --generate_kernel_signature flag and with the provided unit test. I also added two unit tests for the two new cases. Finally, I tried to run the KernelCrashServer autotests, but it doesn't run correctly in developer mode, even before my change. BUG=chromium-os:24632 TEST=manually tested, unit tested, and autotested. No further tests required. Change-Id: Ib65177b06c207675cb7f6e33f1df8fa79dde6090 Reviewed-on: https://gerrit.chromium.org/gerrit/13541 Reviewed-by: Luigi Semenzato Tested-by: Luigi Semenzato Commit-Ready: Luigi Semenzato --- crash_reporter/kernel_collector.cc | 48 ++++++-- crash_reporter/kernel_collector.h | 3 +- crash_reporter/kernel_collector_test.cc | 157 ++++++++++++++++++++++++ 3 files changed, 198 insertions(+), 10 deletions(-) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index bdd1dd203..c600cec0c 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -250,37 +250,47 @@ void KernelCollector::ProcessStackTrace( pcrecpp::StringPiece kernel_dump, bool print_diagnostics, unsigned *hash, - float *last_stack_timestamp) { + float *last_stack_timestamp, + bool *is_watchdog_crash) { pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE()); pcrecpp::RE stack_trace_start_re(kTimestampRegex + " (Call Trace|Backtrace):$"); + // Match lines such as the following and grab out "function_name". + // The ? may or may not be present. + // // For ARM: - // <4>[ 3498.731164] [] (__bug+0x20/0x2c) from [] - // (write_breakme+0xdc/0x1bc) + // <4>[ 3498.731164] [] ? (function_name+0x20/0x2c) from + // [] (foo_bar+0xdc/0x1bc) // // For X86: - // Match lines such as the following and grab out "error_code". - // <4>[ 6066.849504] [<7937bcee>] ? error_code+0x66/0x6c - // The ? may or may not be present + // <4>[ 6066.849504] [<7937bcee>] ? function_name+0x66/0x6c + // pcrecpp::RE stack_entry_re(kTimestampRegex + "\\s+\\[<[[:xdigit:]]+>\\]" // Matches " [<7937bcee>]" "([\\s\\?(]+)" // Matches " ? (" (ARM) or " ? " (X86) "([^\\+ )]+)"); // Matches until delimiter reached std::string line; std::string hashable; + std::string previous_hashable; + bool is_watchdog = false; *hash = 0; *last_stack_timestamp = 0; + // Find the last and second-to-last stack traces. The latter is used when + // the panic is from a watchdog timeout. while (line_re.FindAndConsume(&kernel_dump, &line)) { std::string certainty; std::string function_name; if (stack_trace_start_re.PartialMatch(line, last_stack_timestamp)) { if (print_diagnostics) { - printf("Stack trace starting. Clearing any prior traces.\n"); + printf("Stack trace starting.%s\n", + hashable.empty() ? "" : " Saving prior trace."); } + previous_hashable = hashable; hashable.clear(); + is_watchdog = false; } else if (stack_entry_re.PartialMatch(line, last_stack_timestamp, &certainty, @@ -297,11 +307,28 @@ void KernelCollector::ProcessStackTrace( continue; if (!hashable.empty()) hashable.append("|"); + if (function_name == "watchdog_timer_fn" || + function_name == "watchdog") { + is_watchdog = true; + } hashable.append(function_name); } } + // If the last stack trace contains a watchdog function we assume the panic + // is from the watchdog timer, and we hash the previous stack trace rather + // than the last one, assuming that the previous stack is that of the hung + // thread. + // + // In addition, if the hashable is empty (meaning all frames are uncertain, + // for whatever reason) also use the previous frame, as it cannot be any + // worse. + if (is_watchdog || hashable.empty()) { + hashable = previous_hashable; + } + *hash = HashString(hashable); + *is_watchdog_crash = is_watchdog; if (print_diagnostics) { printf("Hash based on stack trace: \"%s\" at %f.\n", @@ -396,11 +423,13 @@ bool KernelCollector::ComputeKernelStackSignature( unsigned stack_hash = 0; float last_stack_timestamp = 0; std::string human_string; + bool is_watchdog_crash; ProcessStackTrace(kernel_dump, print_diagnostics, &stack_hash, - &last_stack_timestamp); + &last_stack_timestamp, + &is_watchdog_crash); if (!FindCrashingFunction(kernel_dump, print_diagnostics, @@ -422,8 +451,9 @@ bool KernelCollector::ComputeKernelStackSignature( } human_string = human_string.substr(0, kMaxHumanStringLength); - *kernel_signature = StringPrintf("%s-%s-%08X", + *kernel_signature = StringPrintf("%s-%s%s-%08X", kKernelExecName, + (is_watchdog_crash ? "(HANG)-" : ""), human_string.c_str(), stack_hash); return true; diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index e8698a7aa..abbdee998 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -83,7 +83,8 @@ class KernelCollector : public CrashCollector { void ProcessStackTrace(pcrecpp::StringPiece kernel_dump, bool print_diagnostics, unsigned *hash, - float *last_stack_timestamp); + float *last_stack_timestamp, + bool *is_watchdog_crash); bool FindCrashingFunction(pcrecpp::StringPiece kernel_dump, bool print_diagnostics, float stack_trace_timestamp, diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 10e9e3604..87c6f3d92 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -444,6 +444,163 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignatureX86) { &signature, false)); EXPECT_EQ("kernel-Testing panic-E0FC3552", signature); + + // Panic from hung task. + const char kHungTaskBreakMe[] = + "<3>[ 720.459157] INFO: task bash:2287 blocked blah blah\n" + "<5>[ 720.459282] Call Trace:\n" + "<5>[ 720.459307] [<810a457b>] ? __dentry_open+0x186/0x23e\n" + "<5>[ 720.459323] [<810b9c71>] ? mntput_no_expire+0x29/0xe2\n" + "<5>[ 720.459336] [<810b9d48>] ? mntput+0x1e/0x20\n" + "<5>[ 720.459350] [<810ad135>] ? path_put+0x1a/0x1d\n" + "<5>[ 720.459366] [<8137cacc>] schedule+0x4d/0x4f\n" + "<5>[ 720.459379] [<8137ccfb>] schedule_timeout+0x26/0xaf\n" + "<5>[ 720.459394] [<8102127e>] ? should_resched+0xd/0x27\n" + "<5>[ 720.459409] [<81174d1f>] ? _copy_from_user+0x3c/0x50\n" + "<5>[ 720.459423] [<8137cd9e>] " + "schedule_timeout_uninterruptible+0x1a/0x1c\n" + "<5>[ 720.459438] [<810dee63>] write_breakme+0xb3/0x178\n" + "<5>[ 720.459453] [<810dedb0>] ? meminfo_proc_show+0x2f2/0x2f2\n" + "<5>[ 720.459467] [<810d94ae>] proc_reg_write+0x6d/0x87\n" + "<5>[ 720.459481] [<810d9441>] ? proc_reg_poll+0x76/0x76\n" + "<5>[ 720.459493] [<810a5e9e>] vfs_write+0x79/0xa5\n" + "<5>[ 720.459505] [<810a6011>] sys_write+0x40/0x65\n" + "<5>[ 720.459519] [<8137e677>] sysenter_do_call+0x12/0x26\n" + "<0>[ 720.459530] Kernel panic - not syncing: hung_task: blocked tasks\n" + "<5>[ 720.459768] Pid: 31, comm: khungtaskd Tainted: " + "G C 3.0.8 #1\n" + "<5>[ 720.459998] Call Trace:\n" + "<5>[ 720.460140] [<81378a35>] panic+0x53/0x14a\n" + "<5>[ 720.460312] [<8105f875>] watchdog+0x15b/0x1a0\n" + "<5>[ 720.460495] [<8105f71a>] ? hung_task_panic+0x16/0x16\n" + "<5>[ 720.460693] [<81043af3>] kthread+0x67/0x6c\n" + "<5>[ 720.460862] [<81043a8c>] ? __init_kthread_worker+0x2d/0x2d\n" + "<5>[ 720.461106] [<8137eb9e>] kernel_thread_helper+0x6/0x10\n"; + + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kHungTaskBreakMe, + &signature, + false)); + + EXPECT_EQ("kernel-(HANG)-hung_task: blocked tasks-600B37EA", signature); + + // Panic with all question marks in the last stack trace. + const char kUncertainStackTrace[] = + "<0>[56279.689669] ------------[ cut here ]------------\n" + "<2>[56279.689677] kernel BUG at /build/x86-alex/tmp/portage/" + "sys-kernel/chromeos-kernel-0.0.1-r516/work/chromeos-kernel-0.0.1/" + "kernel/timer.c:844!\n" + "<0>[56279.689683] invalid opcode: 0000 [#1] SMP \n" + "<0>[56279.689688] last sysfs file: /sys/power/state\n" + "<5>[56279.689692] Modules linked in: nls_iso8859_1 nls_cp437 vfat fat " + "gobi usbnet tsl2583(C) industrialio(C) snd_hda_codec_realtek " + "snd_hda_intel i2c_dev snd_hda_codec snd_hwdep qcserial snd_pcm usb_wwan " + "i2c_i801 snd_timer nm10_gpio snd_page_alloc rtc_cmos fuse " + "nf_conntrack_ipv6 nf_defrag_ipv6 uvcvideo videodev ip6table_filter " + "ath9k ip6_tables ipv6 mac80211 ath9k_common ath9k_hw ath cfg80211 " + "xt_mark\n" + "<5>[56279.689731] \n" + "<5>[56279.689738] Pid: 24607, comm: powerd_suspend Tainted: G " + "WC 2.6.38.3+ #1 SAMSUNG ELECTRONICS CO., LTD. Alex/G100 \n" + "<5>[56279.689748] EIP: 0060:[<8103e3ea>] EFLAGS: 00210286 CPU: 3\n" + "<5>[56279.689758] EIP is at add_timer+0xd/0x1b\n" + "<5>[56279.689762] EAX: f5e00684 EBX: f5e003c0 ECX: 00000002 EDX: " + "00200246\n" + "<5>[56279.689767] ESI: f5e003c0 EDI: d28bc03c EBP: d2be5e40 ESP: " + "d2be5e40\n" + "<5>[56279.689772] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068\n" + "<0>[56279.689778] Process powerd_suspend (pid: 24607, ti=d2be4000 " + "task=f5dc9b60 task.ti=d2be4000)\n" + "<0>[56279.689782] Stack:\n" + "<5>[56279.689785] d2be5e4c f8dccced f4ac02c0 d2be5e70 f8ddc752 " + "f5e003c0 f4ac0458 f4ac092c\n" + "<5>[56279.689797] f4ac043c f4ac02c0 f4ac0000 f4ac007c d2be5e7c " + "f8dd4a33 f4ac0164 d2be5e94\n" + "<5>[56279.689809] f87e0304 f69ff0cc f4ac0164 f87e02a4 f4ac0164 " + "d2be5eb0 81248968 00000000\n" + "<0>[56279.689821] Call Trace:\n" + "<5>[56279.689840] [] ieee80211_sta_restart+0x25/0x8c " + "[mac80211]\n" + "<5>[56279.689854] [] ieee80211_reconfig+0x2e9/0x339 " + "[mac80211]\n" + "<5>[56279.689869] [] ieee80211_aes_cmac+0x182d/0x184e " + "[mac80211]\n" + "<5>[56279.689883] [] cfg80211_get_dev_from_info+0x29b/0x2c0 " + "[cfg80211]\n" + "<5>[56279.689895] [] ? " + "cfg80211_get_dev_from_info+0x23b/0x2c0 [cfg80211]\n" + "<5>[56279.689904] [<81248968>] legacy_resume+0x25/0x5d\n" + "<5>[56279.689910] [<812490ae>] device_resume+0xdd/0x110\n" + "<5>[56279.689917] [<812491c2>] dpm_resume_end+0xe1/0x271\n" + "<5>[56279.689925] [<81060481>] suspend_devices_and_enter+0x18b/0x1de\n" + "<5>[56279.689932] [<810605ba>] enter_state+0xe6/0x132\n" + "<5>[56279.689939] [<8105fd4b>] state_store+0x91/0x9d\n" + "<5>[56279.689945] [<8105fcba>] ? state_store+0x0/0x9d\n" + "<5>[56279.689953] [<81178fb1>] kobj_attr_store+0x16/0x22\n" + "<5>[56279.689961] [<810eea5e>] sysfs_write_file+0xc1/0xec\n" + "<5>[56279.689969] [<810af443>] vfs_write+0x8f/0x101\n" + "<5>[56279.689975] [<810ee99d>] ? sysfs_write_file+0x0/0xec\n" + "<5>[56279.689982] [<810af556>] sys_write+0x40/0x65\n" + "<5>[56279.689989] [<81002d57>] sysenter_do_call+0x12/0x26\n" + "<0>[56279.689993] Code: c1 d3 e2 4a 89 55 f4 f7 d2 21 f2 6a 00 31 c9 89 " + "d8 e8 6e fd ff ff 5a 8d 65 f8 5b 5e 5d c3 55 89 e5 3e 8d 74 26 00 83 38 " + "00 74 04 <0f> 0b eb fe 8b 50 08 e8 6f ff ff ff 5d c3 55 89 e5 3e 8d 74 " + "26 \n" + "<0>[56279.690009] EIP: [<8103e3ea>] add_timer+0xd/0x1b SS:ESP " + "0068:d2be5e40\n" + "<4>[56279.690113] ---[ end trace b71141bb67c6032a ]---\n" + "<7>[56279.694069] wlan0: deauthenticated from 00:00:00:00:00:01 " + "(Reason: 6)\n" + "<0>[56279.703465] Kernel panic - not syncing: Fatal exception\n" + "<5>[56279.703471] Pid: 24607, comm: powerd_suspend Tainted: G D " + "WC 2.6.38.3+ #1\n" + "<5>[56279.703475] Call Trace:\n" + "<5>[56279.703483] [<8136648c>] ? panic+0x55/0x152\n" + "<5>[56279.703491] [<810057fa>] ? oops_end+0x73/0x81\n" + "<5>[56279.703497] [<81005a44>] ? die+0xed/0xf5\n" + "<5>[56279.703503] [<810033cb>] ? do_trap+0x7a/0x80\n" + "<5>[56279.703509] [<8100369b>] ? do_invalid_op+0x0/0x80\n" + "<5>[56279.703515] [<81003711>] ? do_invalid_op+0x76/0x80\n" + "<5>[56279.703522] [<8103e3ea>] ? add_timer+0xd/0x1b\n" + "<5>[56279.703529] [<81025e23>] ? check_preempt_curr+0x2e/0x69\n" + "<5>[56279.703536] [<8102ef28>] ? ttwu_post_activation+0x5a/0x11b\n" + "<5>[56279.703543] [<8102fa8d>] ? try_to_wake_up+0x213/0x21d\n" + "<5>[56279.703550] [<81368b7f>] ? error_code+0x67/0x6c\n" + "<5>[56279.703557] [<8103e3ea>] ? add_timer+0xd/0x1b\n" + "<5>[56279.703577] [] ? ieee80211_sta_restart+0x25/0x8c " + "[mac80211]\n" + "<5>[56279.703591] [] ? ieee80211_reconfig+0x2e9/0x339 " + "[mac80211]\n" + "<5>[56279.703605] [] ? ieee80211_aes_cmac+0x182d/0x184e " + "[mac80211]\n" + "<5>[56279.703618] [] ? " + "cfg80211_get_dev_from_info+0x29b/0x2c0 [cfg80211]\n" + "<5>[56279.703630] [] ? " + "cfg80211_get_dev_from_info+0x23b/0x2c0 [cfg80211]\n" + "<5>[56279.703637] [<81248968>] ? legacy_resume+0x25/0x5d\n" + "<5>[56279.703643] [<812490ae>] ? device_resume+0xdd/0x110\n" + "<5>[56279.703649] [<812491c2>] ? dpm_resume_end+0xe1/0x271\n" + "<5>[56279.703657] [<81060481>] ? " + "suspend_devices_and_enter+0x18b/0x1de\n" + "<5>[56279.703663] [<810605ba>] ? enter_state+0xe6/0x132\n" + "<5>[56279.703670] [<8105fd4b>] ? state_store+0x91/0x9d\n" + "<5>[56279.703676] [<8105fcba>] ? state_store+0x0/0x9d\n" + "<5>[56279.703683] [<81178fb1>] ? kobj_attr_store+0x16/0x22\n" + "<5>[56279.703690] [<810eea5e>] ? sysfs_write_file+0xc1/0xec\n" + "<5>[56279.703697] [<810af443>] ? vfs_write+0x8f/0x101\n" + "<5>[56279.703703] [<810ee99d>] ? sysfs_write_file+0x0/0xec\n" + "<5>[56279.703709] [<810af556>] ? sys_write+0x40/0x65\n" + "<5>[56279.703716] [<81002d57>] ? sysenter_do_call+0x12/0x26\n"; + + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kUncertainStackTrace, + &signature, + false)); + // The first trace contains only uncertain entries and its hash is 00000000, + // so, if we used that, the signature would be kernel-add_timer-00000000. + // Instead we use the second-to-last trace for the hash. + EXPECT_EQ("kernel-add_timer-B5178878", signature); + ComputeKernelStackSignatureCommon(); } From f13bb58358b879258c5c0eb48c6e09895d394ef1 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Fri, 6 Jan 2012 08:22:07 -0800 Subject: [PATCH 068/225] crash-reporter: Validate proc files and log the state of crashed process. This CL makes the following changes to UserCollector: 1. Check if the maps file is empty after copying proc files to the temporary container directory, which prevents the core-to-minidump conversion failure due to unusable maps file. 2. Obtain and log the state of the crashed process, which helps determine if the crashed process is in the zombie state. 3. Refactor the status file processing code. BUG=chromium-os:24820 TEST=Tested the following: 1. Build crash-reporter and run unit tests. 2. Run the following autotest tests on a Cr48: - logging_CrashSender - logging_UserCrash 3. Check that the process state of a crashed process is logged in /var/log/messages. Change-Id: Iebaf9c36c18185c703c18e7028ee4673dd9ebc93 Reviewed-on: https://gerrit.chromium.org/gerrit/13826 Reviewed-by: Ken Mixter Tested-by: Ben Chan Commit-Ready: Ben Chan --- crash_reporter/user_collector.cc | 79 +++++++++++++++++------ crash_reporter/user_collector.h | 29 ++++++++- crash_reporter/user_collector_test.cc | 91 +++++++++++++++++++++++++-- 3 files changed, 172 insertions(+), 27 deletions(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 01af4eb0a..dffca9d37 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -6,7 +6,6 @@ #include // For struct group. #include -#include #include // For struct passwd. #include // For getpwuid_r, getgrnam_r, WEXITSTATUS. @@ -41,6 +40,7 @@ static const char kCorePipeLimit[] = "4"; static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf"; +static const char kStatePrefix[] = "State:\t"; const char *UserCollector::kUserId = "Uid:\t"; const char *UserCollector::kGroupId = "Gid:\t"; @@ -161,26 +161,30 @@ bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid, return true; } -bool UserCollector::GetIdFromStatus(const char *prefix, - IdKind kind, - const std::string &status_contents, - int *id) { +bool UserCollector::GetFirstLineWithPrefix( + const std::vector &lines, + const char *prefix, std::string *line) { + std::vector::const_iterator line_iterator; + for (line_iterator = lines.begin(); line_iterator != lines.end(); + ++line_iterator) { + if (line_iterator->find(prefix) == 0) { + *line = *line_iterator; + return true; + } + } + return false; +} + +bool UserCollector::GetIdFromStatus( + const char *prefix, IdKind kind, + const std::vector &status_lines, int *id) { // From fs/proc/array.c:task_state(), this file contains: // \nUid:\t\t\t\t\n - std::vector status_lines; - base::SplitString(status_contents, '\n', &status_lines); - std::vector::iterator line_iterator; - for (line_iterator = status_lines.begin(); - line_iterator != status_lines.end(); - ++line_iterator) { - if (line_iterator->find(prefix) == 0) - break; - } - if (line_iterator == status_lines.end()) { + std::string id_line; + if (!GetFirstLineWithPrefix(status_lines, prefix, &id_line)) { return false; } - std::string id_substring = line_iterator->substr(strlen(prefix), - std::string::npos); + std::string id_substring = id_line.substr(strlen(prefix), std::string::npos); std::vector ids; base::SplitString(id_substring, '\t', &ids); if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) { @@ -189,8 +193,19 @@ bool UserCollector::GetIdFromStatus(const char *prefix, const char *number = ids[kind].c_str(); char *end_number = NULL; *id = strtol(number, &end_number, 10); - if (*end_number != '\0') + if (*end_number != '\0') { return false; + } + return true; +} + +bool UserCollector::GetStateFromStatus( + const std::vector &status_lines, std::string *state) { + std::string state_line; + if (!GetFirstLineWithPrefix(status_lines, kStatePrefix, &state_line)) { + return false; + } + *state = state_line.substr(strlen(kStatePrefix), std::string::npos); return true; } @@ -250,6 +265,21 @@ bool UserCollector::CopyOffProcFiles(pid_t pid, return false; } } + return ValidateProcFiles(container_dir); +} + +bool UserCollector::ValidateProcFiles(const FilePath &container_dir) { + // Check if the maps file is empty, which could be due to the crashed + // process being reaped by the kernel before finishing a core dump. + int64 file_size = 0; + if (!file_util::GetFileSize(container_dir.Append("maps"), &file_size)) { + LOG(ERROR) << "Could not get the size of maps file"; + return false; + } + if (file_size == 0) { + LOG(ERROR) << "maps file is empty"; + return false; + } return true; } @@ -269,8 +299,19 @@ bool UserCollector::GetCreatedCrashDirectory(pid_t pid, << file_util::DirectoryExists(process_path); return false; } + + std::vector status_lines; + base::SplitString(status, '\n', &status_lines); + + std::string process_state; + if (!GetStateFromStatus(status_lines, &process_state)) { + LOG(ERROR) << "Could not find process state in status file"; + return false; + } + LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state; + int process_euid; - if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { + if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &process_euid)) { LOG(ERROR) << "Could not find euid in status file"; return false; } diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index d041628de..043d38c09 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -58,7 +58,9 @@ class UserCollector : public CrashCollector { FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPid); FRIEND_TEST(UserCollectorTest, CopyOffProcFilesOK); FRIEND_TEST(UserCollectorTest, GetExecutableBaseNameFromPid); + FRIEND_TEST(UserCollectorTest, GetFirstLineWithPrefix); FRIEND_TEST(UserCollectorTest, GetIdFromStatus); + FRIEND_TEST(UserCollectorTest, GetStateFromStatus); FRIEND_TEST(UserCollectorTest, GetProcessPath); FRIEND_TEST(UserCollectorTest, GetSymlinkTarget); FRIEND_TEST(UserCollectorTest, GetUserInfoFromName); @@ -66,6 +68,7 @@ class UserCollector : public CrashCollector { FRIEND_TEST(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage); FRIEND_TEST(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent); FRIEND_TEST(UserCollectorTest, ShouldDumpUseConsentProductionImage); + FRIEND_TEST(UserCollectorTest, ValidateProcFiles); // Enumeration to pass to GetIdFromStatus. Must match the order // that the kernel lists IDs in the status file. @@ -87,15 +90,37 @@ class UserCollector : public CrashCollector { FilePath *target); bool GetExecutableBaseNameFromPid(uid_t pid, std::string *base_name); + // Returns, via |line|, the first line in |lines| that starts with |prefix|. + // Returns true if a line is found, or false otherwise. + bool GetFirstLineWithPrefix(const std::vector &lines, + const char *prefix, std::string *line); + + // Returns the identifier of |kind|, via |id|, found in |status_lines| on + // the line starting with |prefix|. |status_lines| contains the lines in + // the status file. Returns true if the identifier can be determined. bool GetIdFromStatus(const char *prefix, IdKind kind, - const std::string &status_contents, + const std::vector &status_lines, int *id); + // Returns the process state, via |state|, found in |status_lines|, which + // contains the lines in the status file. Returns true if the process state + // can be determined. + bool GetStateFromStatus(const std::vector &status_lines, + std::string *state); + void LogCollectionError(const std::string &error_message); void EnqueueCollectionErrorLog(pid_t pid, const std::string &exec_name); - bool CopyOffProcFiles(pid_t pid, const FilePath &process_map); + bool CopyOffProcFiles(pid_t pid, const FilePath &container_dir); + + // Validates the proc files at |container_dir| and returns true if they + // are usable for the core-to-minidump conversion later. For instance, if + // a process is reaped by the kernel before the copying of its proc files + // takes place, some proc files like /proc//maps may contain nothing + // and thus become unusable. + bool ValidateProcFiles(const FilePath &container_dir); + // Determines the crash directory for given pid based on pid's owner, // and creates the directory if necessary with appropriate permissions. // Returns true whether or not directory needed to be created, false on diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 7afb712ce..4259f4e65 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -5,6 +5,8 @@ #include #include "base/file_util.h" +#include "base/string_split.h" +#include "base/memory/scoped_temp_dir.h" #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/user_collector.h" @@ -40,6 +42,7 @@ class UserCollectorTest : public ::testing::Test { pid_ = getpid(); chromeos::ClearLog(); } + protected: void ExpectFileEquals(const char *golden, const char *file_path) { @@ -49,6 +52,12 @@ class UserCollectorTest : public ::testing::Test { EXPECT_EQ(golden, contents); } + std::vector SplitLines(const std::string &lines) const { + std::vector result; + base::SplitString(lines, '\n', &result); + return result; + } + UserCollector collector_; pid_t pid_; }; @@ -238,20 +247,54 @@ TEST_F(UserCollectorTest, GetExecutableBaseNameFromPid) { EXPECT_EQ("user_collector_test", base_name); } +TEST_F(UserCollectorTest, GetFirstLineWithPrefix) { + std::vector lines; + std::string line; + + EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Name:", &line)); + EXPECT_EQ("", line); + + lines.push_back("Name:\tls"); + lines.push_back("State:\tR (running)"); + lines.push_back(" Foo:\t1000"); + + line.clear(); + EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, "Name:", &line)); + EXPECT_EQ(lines[0], line); + + line.clear(); + EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, "State:", &line)); + EXPECT_EQ(lines[1], line); + + line.clear(); + EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Foo:", &line)); + EXPECT_EQ("", line); + + line.clear(); + EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, " Foo:", &line)); + EXPECT_EQ(lines[2], line); + + line.clear(); + EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Bar:", &line)); + EXPECT_EQ("", line); +} + TEST_F(UserCollectorTest, GetIdFromStatus) { int id = 1; EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId, UserCollector::kIdEffective, - "nothing here", + SplitLines("nothing here"), &id)); EXPECT_EQ(id, 1); // Not enough parameters. EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId, UserCollector::kIdReal, - "line 1\nUid:\t1\n", &id)); + SplitLines("line 1\nUid:\t1\n"), + &id)); - const char valid_contents[] = "\nUid:\t1\t2\t3\t4\nGid:\t5\t6\t7\t8\n"; + const std::vector valid_contents = + SplitLines("\nUid:\t1\t2\t3\t4\nGid:\t5\t6\t7\t8\n"); EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId, UserCollector::kIdReal, valid_contents, @@ -294,21 +337,36 @@ TEST_F(UserCollectorTest, GetIdFromStatus) { // Fail if junk after number EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId, UserCollector::kIdReal, - "Uid:\t1f\t2\t3\t4\n", + SplitLines("Uid:\t1f\t2\t3\t4\n"), &id)); EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId, UserCollector::kIdReal, - "Uid:\t1\t2\t3\t4\n", + SplitLines("Uid:\t1\t2\t3\t4\n"), &id)); EXPECT_EQ(1, id); // Fail if more than 4 numbers. EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId, UserCollector::kIdReal, - "Uid:\t1\t2\t3\t4\t5\n", + SplitLines("Uid:\t1\t2\t3\t4\t5\n"), &id)); } +TEST_F(UserCollectorTest, GetStateFromStatus) { + std::string state; + EXPECT_FALSE(collector_.GetStateFromStatus(SplitLines("nothing here"), + &state)); + EXPECT_EQ("", state); + + EXPECT_TRUE(collector_.GetStateFromStatus(SplitLines("State:\tR (running)"), + &state)); + EXPECT_EQ("R (running)", state); + + EXPECT_TRUE(collector_.GetStateFromStatus( + SplitLines("Name:\tls\nState:\tZ (zombie)\n"), &state)); + EXPECT_EQ("Z (zombie)", state); +} + TEST_F(UserCollectorTest, GetUserInfoFromName) { gid_t gid = 100; uid_t uid = 100; @@ -353,6 +411,27 @@ TEST_F(UserCollectorTest, CopyOffProcFilesOK) { } } +TEST_F(UserCollectorTest, ValidateProcFiles) { + ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + FilePath container_dir = temp_dir.path(); + + // maps file not exists (i.e. GetFileSize fails) + EXPECT_FALSE(collector_.ValidateProcFiles(container_dir)); + + // maps file is empty + FilePath maps_file = container_dir.Append("maps"); + ASSERT_EQ(0, file_util::WriteFile(maps_file, NULL, 0)); + ASSERT_TRUE(file_util::PathExists(maps_file)); + EXPECT_FALSE(collector_.ValidateProcFiles(container_dir)); + + // maps file is not empty + const char data[] = "test data"; + ASSERT_EQ(sizeof(data), file_util::WriteFile(maps_file, data, sizeof(data))); + ASSERT_TRUE(file_util::PathExists(maps_file)); + EXPECT_TRUE(collector_.ValidateProcFiles(container_dir)); +} + int main(int argc, char **argv) { SetUpTests(&argc, argv, false); return RUN_ALL_TESTS(); From ec7d7835a24884ed0e18317dc07af2fed2a50e64 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Mon, 9 Jan 2012 10:29:58 -0800 Subject: [PATCH 069/225] crash-reporter: Read core from stdin even if proc files are unsuable. This CL fixes commit c7c7914f49fbdef0d1f06c13854daabe176621ed such that UserCollector continues to read the core file from stdin even if the proc files are unusable. BUG=chromium-os:24820 TEST=Tested the following: 1. Build crash-reporter and run unit tests. 2. Run the following autotest tests on a Cr48: - logging_CrashSender - logging_UserCrash Change-Id: I04d09b15a80344b47f0512196c40e94384c5c323 Reviewed-on: https://gerrit.chromium.org/gerrit/13872 Reviewed-by: Ken Mixter Tested-by: Ben Chan Commit-Ready: Ben Chan --- crash_reporter/user_collector.cc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index dffca9d37..308fe9ac9 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -265,7 +265,7 @@ bool UserCollector::CopyOffProcFiles(pid_t pid, return false; } } - return ValidateProcFiles(container_dir); + return true; } bool UserCollector::ValidateProcFiles(const FilePath &container_dir) { @@ -377,11 +377,19 @@ bool UserCollector::ConvertCoreToMinidump(pid_t pid, const FilePath &container_dir, const FilePath &core_path, const FilePath &minidump_path) { - if (!CopyOffProcFiles(pid, container_dir)) { + // If proc files are unuable, we continue to read the core file from stdin, + // but only skip the core-to-minidump conversion, so that we may still use + // the core file for debugging. + bool proc_files_usable = + CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir); + + if (!CopyStdinToCoreFile(core_path)) { return false; } - if (!CopyStdinToCoreFile(core_path)) { + if (!proc_files_usable) { + LOG(INFO) << "Skipped converting core file to minidump due to " + << "unusable proc files"; return false; } From 758e16615a41778fc757dab2ef6e8711669aca2f Mon Sep 17 00:00:00 2001 From: Elly Jones Date: Wed, 11 Jan 2012 15:33:28 -0500 Subject: [PATCH 070/225] [crash-reporter] use libchromeos.pc BUG=chromium-os:24959 TEST=build Change-Id: I62d6e1f19b429c59f07a06372412b360713a16ef Signed-off-by: Elly Jones Reviewed-on: https://gerrit.chromium.org/gerrit/13997 Reviewed-by: Mike Frysinger --- crash_reporter/Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index 9658e7713..05cea91b8 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -21,15 +21,17 @@ TEST_BINS = \ LDCONFIG = $(shell $(PKG_CONFIG) --libs libpcrecpp) +PC_DEPS = glib-2.0 libchromeos $(LIST_PROXIES_PKGS) + # -lglib-2.0 is needed by libbase.a now. -COMMON_LIBS = -lchromeos -lbase -lgflags $(LDCONFIG) \ - $(shell $(PKG_CONFIG) --libs glib-2.0 $(LIST_PROXIES_PKGS)) +COMMON_LIBS = -lbase -lgflags $(LDCONFIG) \ + $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) REPORTER_LIBS = $(COMMON_LIBS) -lmetrics LIST_PROXIES_LIBS = $(COMMON_LIBS) TEST_LIBS = $(COMMON_LIBS) -lgtest -lgmock INCLUDE_DIRS = -I.. -I$(SYSROOT)/usr/include/google-breakpad \ - $(shell $(PKG_CONFIG) --cflags glib-2.0 $(LIST_PROXIES_PKGS)) + $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) CXXFLAGS += -Wall -Werror From b3121a7b7ab2ad9c77281985184f462edc47b26c Mon Sep 17 00:00:00 2001 From: Scott James Remnant Date: Thu, 12 Jan 2012 17:59:03 -0800 Subject: [PATCH 071/225] Revert "[crash-reporter] use libchromeos.pc" This reverts commit 490634ffaefa3b3b93c623e376674ab5cd3763c8 Change-Id: I593c57ccd527d1294439ec730c3b805862fefd70 Reviewed-on: https://gerrit.chromium.org/gerrit/14122 Reviewed-by: Scott James Remnant Tested-by: Scott James Remnant --- crash_reporter/Makefile | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index 05cea91b8..9658e7713 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -21,17 +21,15 @@ TEST_BINS = \ LDCONFIG = $(shell $(PKG_CONFIG) --libs libpcrecpp) -PC_DEPS = glib-2.0 libchromeos $(LIST_PROXIES_PKGS) - # -lglib-2.0 is needed by libbase.a now. -COMMON_LIBS = -lbase -lgflags $(LDCONFIG) \ - $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) +COMMON_LIBS = -lchromeos -lbase -lgflags $(LDCONFIG) \ + $(shell $(PKG_CONFIG) --libs glib-2.0 $(LIST_PROXIES_PKGS)) REPORTER_LIBS = $(COMMON_LIBS) -lmetrics LIST_PROXIES_LIBS = $(COMMON_LIBS) TEST_LIBS = $(COMMON_LIBS) -lgtest -lgmock INCLUDE_DIRS = -I.. -I$(SYSROOT)/usr/include/google-breakpad \ - $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) + $(shell $(PKG_CONFIG) --cflags glib-2.0 $(LIST_PROXIES_PKGS)) CXXFLAGS += -Wall -Werror From 91ad60a7fc7660ea96e48c8feb2943aefb9a0590 Mon Sep 17 00:00:00 2001 From: Elly Jones Date: Wed, 11 Jan 2012 15:33:28 -0500 Subject: [PATCH 072/225] [crash-reporter] use libchromeos.pc BUG=chromium-os:24959 TEST=build Signed-off-by: Elly Jones Reviewed-on: https://gerrit.chromium.org/gerrit/13997 Reviewed-by: Mike Frysinger (cherry picked from commit 6f7fb4aea569bb597c94194be1ad611b37ffcf40) Change-Id: I75ea31a75d2e67facc2a38a9aabda2ae81b702a6 Reviewed-on: https://gerrit.chromium.org/gerrit/14406 Reviewed-by: Mike Frysinger Commit-Ready: Elly Jones Tested-by: Elly Jones --- crash_reporter/Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index 9658e7713..05cea91b8 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -21,15 +21,17 @@ TEST_BINS = \ LDCONFIG = $(shell $(PKG_CONFIG) --libs libpcrecpp) +PC_DEPS = glib-2.0 libchromeos $(LIST_PROXIES_PKGS) + # -lglib-2.0 is needed by libbase.a now. -COMMON_LIBS = -lchromeos -lbase -lgflags $(LDCONFIG) \ - $(shell $(PKG_CONFIG) --libs glib-2.0 $(LIST_PROXIES_PKGS)) +COMMON_LIBS = -lbase -lgflags $(LDCONFIG) \ + $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) REPORTER_LIBS = $(COMMON_LIBS) -lmetrics LIST_PROXIES_LIBS = $(COMMON_LIBS) TEST_LIBS = $(COMMON_LIBS) -lgtest -lgmock INCLUDE_DIRS = -I.. -I$(SYSROOT)/usr/include/google-breakpad \ - $(shell $(PKG_CONFIG) --cflags glib-2.0 $(LIST_PROXIES_PKGS)) + $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) CXXFLAGS += -Wall -Werror From 4b37d0ed651668c0f09cd65bbd64b22ded6526c0 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 6 Feb 2012 16:47:56 -0500 Subject: [PATCH 073/225] [crash-reporter] drop unused crontab fragment The ebuild no longer installs this, so punt it. BUG=chromium-os:25750 TEST=`emerge crash-reporter` still works Change-Id: I94416b44ee87cc282a80128f5a3b2f190a191184 Reviewed-on: https://gerrit.chromium.org/gerrit/15402 Reviewed-by: Scott James Remnant Reviewed-by: Ben Chan Commit-Ready: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_sender.hourly | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 crash_reporter/crash_sender.hourly diff --git a/crash_reporter/crash_sender.hourly b/crash_reporter/crash_sender.hourly deleted file mode 100644 index 09c815193..000000000 --- a/crash_reporter/crash_sender.hourly +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -set -e - -# Background the sender so it can trickle out crash dumps. It will -# exit early if already running. -/sbin/crash_sender 2>&1 & From d3ac79663fdba1e4c31a3227def22b8555c5ff6f Mon Sep 17 00:00:00 2001 From: Chris Masone Date: Fri, 24 Feb 2012 14:38:57 -0800 Subject: [PATCH 074/225] [crash-reporter] Move flag files to /var/run from /tmp The kernel-crash-detected and unclean-shutdown-detected flag files needed to move out of /tmp to protect against symlink attacks by a non-root user. /var/run is owned by root, so non-privileged users can't write there. BUG=chromium-os:17085 TEST=unit TEST=logging_CrashSender,logging_UserCrash STATUS=Fixed Change-Id: Ibf9b314f1a03ee984eea2acf6007c120aa300dd1 Reviewed-on: https://gerrit.chromium.org/gerrit/16751 Tested-by: Chris Masone Reviewed-by: Kees Cook Commit-Ready: Chris Masone --- crash_reporter/crash_reporter.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 42998e953..ac3964bdf 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -32,6 +32,9 @@ static const char kUserCrashSignal[] = "org.chromium.CrashReporter.UserCrash"; static const char kUncleanShutdownFile[] = "/var/lib/crash_reporter/pending_clean_shutdown"; +static const char kKernelCrashDetected[] = "/var/run/kernel-crash-detected"; +static const char kUncleanShutdownDetected[] = + "/var/run/unclean-shutdown-detected"; // Enumeration of kinds of crashes to be used in the CrashCounter histogram. @@ -121,11 +124,11 @@ static int Initialize(KernelCollector *kernel_collector, // the last kernel crash. if (IsFeedbackAllowed()) { if (was_kernel_crash) { - TouchFile(FilePath("/tmp/kernel-crash-detected")); + TouchFile(FilePath(kKernelCrashDetected)); } else if (was_unclean_shutdown) { // We only count an unclean shutdown if it did not come with // an associated kernel crash. - TouchFile(FilePath("/tmp/unclean-shutdown-detected")); + TouchFile(FilePath(kUncleanShutdownDetected)); } } From 5825d5a64241df397e741acb68d3ff81ce8d40ca Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 22 Feb 2012 17:15:15 -0800 Subject: [PATCH 075/225] Remove collected crash report from pstore Make sure that once collected, a crash report is removed from the pstore filesystem so that the memory is cleared to avoid endless crash duplicates. BUG=chromium-os:26496 TEST=lumpy build & boot, triggered crashes ("echo bug > /proc/breakme") are saved to /var/spool/crash and removed from pstore. STATUS=Fixed Change-Id: Ic224f5880cdc34d1f75470ac71f27fcad146ff4b Signed-off-by: Kees Cook Reviewed-on: https://gerrit.chromium.org/gerrit/16501 Reviewed-by: Olof Johansson Reviewed-by: Bryan Freed --- crash_reporter/kernel_collector.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index c600cec0c..6f2855ae1 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -80,8 +80,9 @@ bool KernelCollector::ReadRecordToString(std::string *contents, } if (record_re.FullMatch(record, &captured)){ - // Found a match, append it to the content. + // Found a match, append it to the content, and remove from pstore. contents->append(captured); + file_util::Delete(ramoops_record, false); *record_found = true; } else { *record_found = false; From 2076b9011d0f217d2c308d28ba13af84cf2e4b17 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Wed, 29 Feb 2012 22:26:54 -0800 Subject: [PATCH 076/225] Fix KernelCollector code and unit tests. This CL fixes the KernelCollectorTest.LoadPreservedDump test due to KernelCollector::records_ not being initialized. It also updates KernelCollectorTest.CollectPreservedFileMissing and KernelCollectorTest.CollectNoCrash. BUG=chromium-os:27126 TEST=Tested the following: 1. emerge-{x86,amd64,arm}-generic crash-reporter 2. FEATURES="test" emerge-{x86,amd64,arm}-generic crash-reporter Change-Id: If5a773fa77a2e66e539d204410140d1b4e66d062 Reviewed-on: https://gerrit.chromium.org/gerrit/17125 Reviewed-by: Kees Cook Commit-Ready: Ben Chan Tested-by: Ben Chan --- crash_reporter/kernel_collector.cc | 3 ++- crash_reporter/kernel_collector_test.cc | 22 ++++++++-------------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 6f2855ae1..d23c59591 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -48,7 +48,8 @@ COMPILE_ASSERT(arraysize(s_pc_regex) == KernelCollector::archCount, KernelCollector::KernelCollector() : is_enabled_(false), - ramoops_dump_path_(kDumpPath) { + ramoops_dump_path_(kDumpPath), + records_(0) { // We expect crash dumps in the format of the architecture we are built for. arch_ = GetCompilerArch(); } diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 87c6f3d92..873a5a7cd 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -28,14 +28,6 @@ bool IsMetrics() { return s_metrics; } -class TKernelCollector : public KernelCollector { - bool LoadParameters() { - // Since we don't have /sys/module/ramoops/parameters on our system just - // return true instead of getting the parameters from the files. - return true; - } -}; - class KernelCollectorTest : public ::testing::Test { void SetUp() { s_crashes = 0; @@ -61,13 +53,13 @@ class KernelCollectorTest : public ::testing::Test { void SetUpSuccessfulCollect(); void ComputeKernelStackSignatureCommon(); - TKernelCollector collector_; + KernelCollector collector_; FilePath test_kcrash_; }; TEST_F(KernelCollectorTest, ComputeKernelStackSignatureBase) { // Make sure the normal build architecture is detected - EXPECT_TRUE(collector_.GetArch() != TKernelCollector::archUnknown); + EXPECT_TRUE(collector_.GetArch() != KernelCollector::archUnknown); } TEST_F(KernelCollectorTest, LoadPreservedDump) { @@ -76,10 +68,12 @@ TEST_F(KernelCollectorTest, LoadPreservedDump) { dump.clear(); WriteStringToFile(test_kcrash_, "emptydata"); + ASSERT_TRUE(collector_.LoadParameters()); ASSERT_FALSE(collector_.LoadPreservedDump(&dump)); ASSERT_EQ("", dump); WriteStringToFile(test_kcrash_, "====1.1\nsomething"); + ASSERT_TRUE(collector_.LoadParameters()); ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); ASSERT_EQ("something", dump); } @@ -219,15 +213,15 @@ TEST_F(KernelCollectorTest, StripSensitiveDataSample) { TEST_F(KernelCollectorTest, CollectPreservedFileMissing) { ASSERT_FALSE(collector_.Collect()); - ASSERT_TRUE(FindLog("Unable to open")); - ASSERT_TRUE(FindLog("No valid records found")); + ASSERT_FALSE(FindLog("Stored kcrash to ")); ASSERT_EQ(0, s_crashes); } TEST_F(KernelCollectorTest, CollectNoCrash) { WriteStringToFile(test_kcrash_, ""); ASSERT_FALSE(collector_.Collect()); - ASSERT_FALSE(FindLog("Collected kernel crash")); + ASSERT_TRUE(FindLog("No valid records found")); + ASSERT_FALSE(FindLog("Stored kcrash to ")); ASSERT_EQ(0, s_crashes); } @@ -353,7 +347,7 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignatureARM) { "[] (proc_reg_write+0x88/0x9c)\n"; std::string signature; - collector_.SetArch(TKernelCollector::archArm); + collector_.SetArch(KernelCollector::archArm); EXPECT_TRUE( collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false)); EXPECT_EQ("kernel-write_breakme-97D3E92F", signature); From 6e709a11031c80c96f47da50b3231074334f462c Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Wed, 29 Feb 2012 12:10:44 -0800 Subject: [PATCH 077/225] Improve error diagnostics in UserCollector. This CL makes the following changes to improve error diagnostics in UserCollector: 1. Add an error type to describe why the crash reporter fails to collects a user crash, which is reported to the crash server along with the "crash_reporter-user-collection" error signature. 2. Perform some preliminary validations of the core file before converting it to a minidump file using core2md. 3. Identify the case when a 32-bit core file is produced on a 64-bit platform as core2md currently cannot handle 32-bit core files on a 64-bit platform. BUG=chromium-os:7871 TEST=Tested the following: 1. emerge-{x86,amd64,arm}-generic crash-reporter 2. FEATURES="test" emerge-{x86,amd64,arm}-generic crash-reporter 3. Run the following autotest tests on x86-mario and stumpy64: - logging_CrashSender - logging_UserCrash Change-Id: Ib50b4cc81a91f7cd75f9440005200d4027ce1f6f Reviewed-on: https://gerrit.chromium.org/gerrit/17166 Reviewed-by: Michael Krebs Tested-by: Ben Chan Commit-Ready: Ben Chan --- crash_reporter/user_collector.cc | 117 ++++++++++++++++++++------ crash_reporter/user_collector.h | 40 +++++++-- crash_reporter/user_collector_test.cc | 49 +++++++++++ 3 files changed, 171 insertions(+), 35 deletions(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 308fe9ac9..28c5ce6c9 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -4,6 +4,9 @@ #include "crash-reporter/user_collector.h" +#include +#include +#include #include // For struct group. #include #include // For struct passwd. @@ -67,6 +70,25 @@ void UserCollector::Initialize( UserCollector::~UserCollector() { } +std::string UserCollector::GetErrorTypeSignature(ErrorType error_type) const { + switch (error_type) { + case kErrorSystemIssue: + return "system-issue"; + case kErrorReadCoreData: + return "read-core-data"; + case kErrorUnusableProcFiles: + return "unusable-proc-files"; + case kErrorInvalidCoreFile: + return "invalid-core-file"; + case kErrorUnsupported32BitCoreFile: + return "unsupported-32bit-core-file"; + case kErrorCore2MinidumpConversion: + return "core2md-conversion"; + default: + return ""; + } +} + std::string UserCollector::GetPattern(bool enabled) const { if (enabled) { // Combine the three crash attributes into one parameter to try to reduce @@ -210,6 +232,7 @@ bool UserCollector::GetStateFromStatus( } void UserCollector::EnqueueCollectionErrorLog(pid_t pid, + ErrorType error_type, const std::string &exec) { FilePath crash_path; LOG(INFO) << "Writing conversion problems as separate crash report."; @@ -237,6 +260,7 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid, // might have created. WriteNewFile(log_path, error_log.data(), error_log.length()); AddCrashMetaData("sig", kCollectionErrorSignature); + AddCrashMetaData("error_type", GetErrorTypeSignature(error_type)); WriteCrashMetaData(meta_path, exec, log_path.value()); } @@ -268,7 +292,7 @@ bool UserCollector::CopyOffProcFiles(pid_t pid, return true; } -bool UserCollector::ValidateProcFiles(const FilePath &container_dir) { +bool UserCollector::ValidateProcFiles(const FilePath &container_dir) const { // Check if the maps file is empty, which could be due to the crashed // process being reaped by the kernel before finishing a core dump. int64 file_size = 0; @@ -283,6 +307,41 @@ bool UserCollector::ValidateProcFiles(const FilePath &container_dir) { return true; } +UserCollector::ErrorType UserCollector::ValidateCoreFile( + const FilePath &core_path) const { + int fd = HANDLE_EINTR(open(core_path.value().c_str(), O_RDONLY)); + if (fd < 0) { + LOG(ERROR) << "Could not open core file " << core_path.value(); + return kErrorInvalidCoreFile; + } + + char e_ident[EI_NIDENT]; + bool read_ok = file_util::ReadFromFD(fd, e_ident, sizeof(e_ident)); + HANDLE_EINTR(close(fd)); + if (!read_ok) { + LOG(ERROR) << "Could not read header of core file"; + return kErrorInvalidCoreFile; + } + + if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || + e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) { + LOG(ERROR) << "Invalid core file"; + return kErrorInvalidCoreFile; + } + +#if __WORDSIZE == 64 + // TODO(benchan, mkrebs): Remove this check once core2md can + // handles both 32-bit and 64-bit ELF on a 64-bit platform. + if (e_ident[EI_CLASS] == ELFCLASS32) { + LOG(ERROR) << "Conversion of 32-bit core file on 64-bit platform is " + << "currently not supported"; + return kErrorUnsupported32BitCoreFile; + } +#endif + + return kErrorNone; +} + bool UserCollector::GetCreatedCrashDirectory(pid_t pid, FilePath *crash_file_path, bool *out_of_capacity) { @@ -373,10 +432,11 @@ bool UserCollector::RunCoreToMinidump(const FilePath &core_path, return true; } -bool UserCollector::ConvertCoreToMinidump(pid_t pid, - const FilePath &container_dir, - const FilePath &core_path, - const FilePath &minidump_path) { +UserCollector::ErrorType UserCollector::ConvertCoreToMinidump( + pid_t pid, + const FilePath &container_dir, + const FilePath &core_path, + const FilePath &minidump_path) { // If proc files are unuable, we continue to read the core file from stdin, // but only skip the core-to-minidump conversion, so that we may still use // the core file for debugging. @@ -384,35 +444,37 @@ bool UserCollector::ConvertCoreToMinidump(pid_t pid, CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir); if (!CopyStdinToCoreFile(core_path)) { - return false; + return kErrorReadCoreData; } if (!proc_files_usable) { LOG(INFO) << "Skipped converting core file to minidump due to " << "unusable proc files"; - return false; + return kErrorUnusableProcFiles; } - bool conversion_result = RunCoreToMinidump( - core_path, - container_dir, // procfs directory - minidump_path, - container_dir); // temporary directory - - if (conversion_result) { - LOG(INFO) << "Stored minidump to " << minidump_path.value(); + ErrorType error = ValidateCoreFile(core_path); + if (error != kErrorNone) { + return error; } - return conversion_result; + if (!RunCoreToMinidump(core_path, + container_dir, // procfs directory + minidump_path, + container_dir)) { // temporary directory + return kErrorCore2MinidumpConversion; + } + + LOG(INFO) << "Stored minidump to " << minidump_path.value(); + return kErrorNone; } -bool UserCollector::ConvertAndEnqueueCrash(int pid, - const std::string &exec, - bool *out_of_capacity) { +UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash( + int pid, const std::string &exec, bool *out_of_capacity) { FilePath crash_path; if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) { LOG(ERROR) << "Unable to find/create process-specific crash path"; - return false; + return kErrorSystemIssue; } // Directory like /tmp/crash_reporter/1234 which contains the @@ -431,11 +493,12 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid, if (GetLogContents(FilePath(kDefaultLogConfig), exec, log_path)) AddCrashMetaData("log", log_path.value()); - if (!ConvertCoreToMinidump(pid, container_dir, core_path, - minidump_path)) { + ErrorType error_type = + ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path); + if (error_type != kErrorNone) { LOG(INFO) << "Leaving core file at " << core_path.value() << " due to conversion error"; - return false; + return error_type; } // Here we commit to sending this file. We must not return false @@ -453,7 +516,7 @@ bool UserCollector::ConvertAndEnqueueCrash(int pid, } file_util::Delete(container_dir, true); - return true; + return kErrorNone; } bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes, @@ -546,11 +609,11 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, if (generate_diagnostics_) { bool out_of_capacity = false; - bool convert_and_enqueue_result = + ErrorType error_type = ConvertAndEnqueueCrash(pid, exec, &out_of_capacity); - if (!convert_and_enqueue_result) { + if (error_type != kErrorNone) { if (!out_of_capacity) - EnqueueCollectionErrorLog(pid, exec); + EnqueueCollectionErrorLog(pid, error_type, exec); return false; } } diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 043d38c09..01ca2afd2 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -69,6 +69,7 @@ class UserCollector : public CrashCollector { FRIEND_TEST(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent); FRIEND_TEST(UserCollectorTest, ShouldDumpUseConsentProductionImage); FRIEND_TEST(UserCollectorTest, ValidateProcFiles); + FRIEND_TEST(UserCollectorTest, ValidateCoreFile); // Enumeration to pass to GetIdFromStatus. Must match the order // that the kernel lists IDs in the status file. @@ -80,8 +81,23 @@ class UserCollector : public CrashCollector { kIdMax }; + enum ErrorType { + kErrorNone, + kErrorSystemIssue, + kErrorReadCoreData, + kErrorUnusableProcFiles, + kErrorInvalidCoreFile, + kErrorUnsupported32BitCoreFile, + kErrorCore2MinidumpConversion, + }; + static const int kForkProblem = 255; + // Returns an error type signature for a given |error_type| value, + // which is reported to the crash server along with the + // crash_reporter-user-collection signature. + std::string GetErrorTypeSignature(ErrorType error_type) const; + std::string GetPattern(bool enabled) const; bool SetUpInternal(bool enabled); @@ -110,7 +126,8 @@ class UserCollector : public CrashCollector { std::string *state); void LogCollectionError(const std::string &error_message); - void EnqueueCollectionErrorLog(pid_t pid, const std::string &exec_name); + void EnqueueCollectionErrorLog(pid_t pid, ErrorType error_type, + const std::string &exec_name); bool CopyOffProcFiles(pid_t pid, const FilePath &container_dir); @@ -119,7 +136,14 @@ class UserCollector : public CrashCollector { // a process is reaped by the kernel before the copying of its proc files // takes place, some proc files like /proc//maps may contain nothing // and thus become unusable. - bool ValidateProcFiles(const FilePath &container_dir); + bool ValidateProcFiles(const FilePath &container_dir) const; + + // Validates the core file at |core_path| and returns kErrorNone if + // the file contains the ELF magic bytes and an ELF class that matches the + // platform (i.e. 32-bit ELF on a 32-bit platform or 64-bit ELF on a 64-bit + // platform), which is due to the limitation in core2md. It returns an error + // type otherwise. + ErrorType ValidateCoreFile(const FilePath &core_path) const; // Determines the crash directory for given pid based on pid's owner, // and creates the directory if necessary with appropriate permissions. @@ -133,12 +157,12 @@ class UserCollector : public CrashCollector { const FilePath &procfs_directory, const FilePath &minidump_path, const FilePath &temp_directory); - bool ConvertCoreToMinidump(pid_t pid, - const FilePath &container_dir, - const FilePath &core_path, - const FilePath &minidump_path); - bool ConvertAndEnqueueCrash(int pid, const std::string &exec_name, - bool *out_of_capacity); + ErrorType ConvertCoreToMinidump(pid_t pid, + const FilePath &container_dir, + const FilePath &core_path, + const FilePath &minidump_path); + ErrorType ConvertAndEnqueueCrash(int pid, const std::string &exec_name, + bool *out_of_capacity); bool ParseCrashAttributes(const std::string &crash_attributes, pid_t *pid, int *signal, std::string *kernel_supplied_name); diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 4259f4e65..2d3ac0c76 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include +#include #include #include "base/file_util.h" @@ -432,6 +434,53 @@ TEST_F(UserCollectorTest, ValidateProcFiles) { EXPECT_TRUE(collector_.ValidateProcFiles(container_dir)); } +TEST_F(UserCollectorTest, ValidateCoreFile) { + ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + FilePath container_dir = temp_dir.path(); + FilePath core_file = container_dir.Append("core"); + + // Core file does not exist + EXPECT_EQ(UserCollector::kErrorInvalidCoreFile, + collector_.ValidateCoreFile(core_file)); + char e_ident[EI_NIDENT]; + e_ident[EI_MAG0] = ELFMAG0; + e_ident[EI_MAG1] = ELFMAG1; + e_ident[EI_MAG2] = ELFMAG2; + e_ident[EI_MAG3] = ELFMAG3; +#if __WORDSIZE == 32 + e_ident[EI_CLASS] = ELFCLASS32; +#elif __WORDSIZE == 64 + e_ident[EI_CLASS] = ELFCLASS64; +#else +#error Unknown/unsupported value of __WORDSIZE. +#endif + + // Core file has the expected header + ASSERT_TRUE(file_util::WriteFile(core_file, e_ident, sizeof(e_ident))); + EXPECT_EQ(UserCollector::kErrorNone, + collector_.ValidateCoreFile(core_file)); + +#if __WORDSIZE == 64 + // 32-bit core file on 64-bit platform + e_ident[EI_CLASS] = ELFCLASS32; + ASSERT_TRUE(file_util::WriteFile(core_file, e_ident, sizeof(e_ident))); + EXPECT_EQ(UserCollector::kErrorUnsupported32BitCoreFile, + collector_.ValidateCoreFile(core_file)); + e_ident[EI_CLASS] = ELFCLASS64; +#endif + + // Invalid core files + ASSERT_TRUE(file_util::WriteFile(core_file, e_ident, sizeof(e_ident) - 1)); + EXPECT_EQ(UserCollector::kErrorInvalidCoreFile, + collector_.ValidateCoreFile(core_file)); + + e_ident[EI_MAG0] = 0; + ASSERT_TRUE(file_util::WriteFile(core_file, e_ident, sizeof(e_ident))); + EXPECT_EQ(UserCollector::kErrorInvalidCoreFile, + collector_.ValidateCoreFile(core_file)); +} + int main(int argc, char **argv) { SetUpTests(&argc, argv, false); return RUN_ALL_TESTS(); From c55dbfd6d80e9536608ad7bbd7947f2d3d0d7138 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 9 Mar 2012 14:06:24 -0500 Subject: [PATCH 078/225] clean up makefile and convert to SLOT-ed libbase This includes a couple of fixes: - use the right compiler flag names - execute pkg-config twice total, and not once per compile - build against SLOT-ed libbase BUG=chromium-os:16623 TEST=`emerge-x86-alex crash-reporter` still works TEST=`cros_run_unit_tests --board x86-alex -p crash-reporter` passes Change-Id: I76fdf552de1c0e10367475f3ad22b6b0b33bfa66 Reviewed-on: https://gerrit.chromium.org/gerrit/17709 Reviewed-by: Ben Chan Commit-Ready: Mike Frysinger Tested-by: Mike Frysinger Reviewed-by: Kees Cook --- crash_reporter/Makefile | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index 05cea91b8..9a25a8839 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -2,6 +2,9 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +BASE_VER = 85268 +PKG_CONFIG ?= pkg-config + CRASH_REPORTER = crash_reporter LIST_PROXIES_BIN = list_proxies REPORTER_BINS = $(CRASH_REPORTER) $(LIST_PROXIES_BIN) @@ -19,37 +22,35 @@ TEST_BINS = \ unclean_shutdown_collector_test \ user_collector_test -LDCONFIG = $(shell $(PKG_CONFIG) --libs libpcrecpp) +PC_DEPS = glib-2.0 $(LIST_PROXIES_PKGS) libpcrecpp \ + libchrome-$(BASE_VER) libchromeos-$(BASE_VER) +PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) +PC_LIBS := $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) -PC_DEPS = glib-2.0 libchromeos $(LIST_PROXIES_PKGS) - -# -lglib-2.0 is needed by libbase.a now. -COMMON_LIBS = -lbase -lgflags $(LDCONFIG) \ - $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) +COMMON_LIBS = -lgflags $(PC_LIBS) REPORTER_LIBS = $(COMMON_LIBS) -lmetrics LIST_PROXIES_LIBS = $(COMMON_LIBS) TEST_LIBS = $(COMMON_LIBS) -lgtest -lgmock -INCLUDE_DIRS = -I.. -I$(SYSROOT)/usr/include/google-breakpad \ - $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) +CPPFLAGS += -I.. -I$(SYSROOT)/usr/include/google-breakpad $(PC_CFLAGS) CXXFLAGS += -Wall -Werror all: $(REPORTER_BINS) $(CRASH_REPORTER): crash_reporter.o $(CRASH_OBJS) - $(CXX) $(CXXFLAGS) $^ $(REPORTER_LIBS) -o $@ + $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ $(REPORTER_LIBS) -o $@ $(LIST_PROXIES_BIN): $(LIST_PROXIES_OBJS) - $(CXX) $(CXXFLAGS) $^ $(LIST_PROXIES_LIBS) -o $@ + $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ $(LIST_PROXIES_LIBS) -o $@ tests: $(TEST_BINS) %_test: %_test.o $(TEST_OBJS) - $(CXX) $(CXXFLAGS) $(LIB_DIRS) $^ $(TEST_LIBS) -o $@ + $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ $(TEST_LIBS) -o $@ .cc.o: - $(CXX) $(CXXFLAGS) $(INCLUDE_DIRS) -c $< -o $@ + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ clean: rm -rf *.o $(CRASH_BIN) $(LIST_PROXIES_BIN) $(TEST_BINS) From 557bf456f7e58ad068ba13339b0877a4c6bc37a1 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Wed, 14 Mar 2012 16:51:01 -0700 Subject: [PATCH 079/225] Update crash_reporter_logs.conf to collect log files for cros-installer. BUG=chromium-os:27821 TEST=emerge crash-reporter and verified the updated crash_reporter_logs.conf is installed. Change-Id: Id2b7de30b36eaa57e6251261d8bca6810d5e8b82 Reviewed-on: https://gerrit.chromium.org/gerrit/18181 Reviewed-by: Don Garrett Reviewed-by: Michael Krebs Tested-by: Ben Chan Commit-Ready: Ben Chan --- crash_reporter/crash_reporter_logs.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 62c6216a0..c91d4d595 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -16,6 +16,9 @@ # the crash to parent processes, so commands should execute quickly. # update_engine:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/update_engine/.) | tail -c 50000 +# The cros_installer output is logged into the update engine log file, +# so it is handled in the same way as update_engine. +cros_installer:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/update_engine/.) | tail -c 50000 # The following rule is used for generating additional diagnostics when # collection of user crashes fails. This output should not be too large From b87375957eb9e07c929a84683460e9e7f87985c1 Mon Sep 17 00:00:00 2001 From: Bryan Freed Date: Mon, 2 Apr 2012 17:05:48 -0700 Subject: [PATCH 080/225] Better support 64bit x86 kernel crashes. FindCrashingFunction() fails to find the crashing kernel function because the x86 64bit kernel code (arch/x86/kernel/dumpstack.c:die()) issues "RIP" instead of "EIP". So it falls back to using the panic string in the crash signature. This causes the logging_KernelCrash.py test to fail because it looks for the crashing function in the signature string. BUG=chromium-os:22353 TEST=logging_KernelCrash Change-Id: Ic4790648facf5f0be2b82001f8e18b606eca16c7 Reviewed-on: https://gerrit.chromium.org/gerrit/19492 Reviewed-by: Ben Chan Tested-by: Bryan Freed Commit-Ready: Bryan Freed --- crash_reporter/kernel_collector.cc | 3 +++ crash_reporter/kernel_collector.h | 1 + 2 files changed, 4 insertions(+) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index d23c59591..0895b4eb3 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -41,6 +41,7 @@ static const char *s_pc_regex[] = { 0, " PC is at ([^\\+ ]+).*", " EIP: \\[<.*>\\] ([^\\+ ]+).*", // X86 uses EIP for the program counter + " RIP \\[<.*>\\] ([^\\+ ]+).*", // X86_64 uses RIP for the program counter }; COMPILE_ASSERT(arraysize(s_pc_regex) == KernelCollector::archCount, @@ -342,6 +343,8 @@ enum KernelCollector::ArchKind KernelCollector::GetCompilerArch(void) { #if defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY) return archArm; +#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_64) + return archX86_64; #elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY) return archX86; #else diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index abbdee998..b4f3c1a70 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -23,6 +23,7 @@ class KernelCollector : public CrashCollector { archUnknown, archArm, archX86, + archX86_64, archCount // Number of architectures. }; From fff57dbc2e0e1b60606245f52aedd20df7dd49e2 Mon Sep 17 00:00:00 2001 From: Elly Jones Date: Fri, 6 Apr 2012 14:18:15 -0400 Subject: [PATCH 081/225] [crash-reporter] add OWNERS TEST=None BUG=chromium-os:22007 Change-Id: I96e3743c5428ecb4fe628dfad1862659ddbd9570 Signed-off-by: Elly Jones Reviewed-on: https://gerrit.chromium.org/gerrit/19763 Reviewed-by: Ben Chan --- crash_reporter/OWNERS | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 crash_reporter/OWNERS diff --git a/crash_reporter/OWNERS b/crash_reporter/OWNERS new file mode 100644 index 000000000..8c1ac9e10 --- /dev/null +++ b/crash_reporter/OWNERS @@ -0,0 +1,2 @@ +benchan@chromium.org +mkrebs@chromium.org From e1b21e95d366380aa5284824a250256df6bbd721 Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Wed, 4 Apr 2012 20:14:11 -0700 Subject: [PATCH 082/225] crash-reporter: Send "error_type" field for crash-reporter errors If the "error_type" field was set by crash_reporter (see benchan@'s https://gerrit.chromium.org/gerrit/17166), then send it with the crash report. This currently only affects crashes with the "crash_reporter-user-collection" error signature. BUG=chromium-os:7871 TEST=Manually ran crash_sender Change-Id: I34c5e055efb407d57a3f83b07e8d4fc0a6b8fac1 Reviewed-on: https://gerrit.chromium.org/gerrit/19641 Tested-by: Michael Krebs Reviewed-by: Ben Chan Commit-Ready: Michael Krebs --- crash_reporter/crash_sender | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index a7a2fc46e..4b8473bfb 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -233,6 +233,12 @@ send_crash() { extra_value1="@${log}" fi + local error_type="$(get_key_value "${meta_path}" "error_type")" + if [ "${error_type}" != "undefined" ]; then + extra_key3="error_type" + extra_value3="${error_type}" + fi + lecho "Sending crash:" lecho " Scheduled to send in ${sleep_time}s" lecho " Metadata: ${meta_path} (${kind})" @@ -245,6 +251,9 @@ send_crash() { lecho " HWClass: ${hwclass}" lecho " ${extra_key1}: ${extra_value1}" lecho " ${extra_key2}: ${extra_value2}" + if [ -n "${extra_key3}" ]; then + lecho " ${extra_key3}: ${extra_value3}" + fi fi lecho " Exec name: ${exec_name}" if is_mock; then @@ -284,6 +293,7 @@ send_crash() { -F "exec_name=${exec_name}" \ -F "${extra_key1}=${extra_value1}" \ -F "${extra_key2}=${extra_value2}" \ + ${extra_key3:+-F "${extra_key3}=${extra_value3}"} \ -F "guid=<${CONSENT_ID}" -o "${report_id}" 2>"${curl_stderr}" curl_result=$? set -e From f70060c5e610ed0aa648dcb6aaea5d859834b8ba Mon Sep 17 00:00:00 2001 From: Simon Que Date: Mon, 9 Apr 2012 19:07:07 -0700 Subject: [PATCH 083/225] Add udev event-based crash reporting This allows crash_reporter to handle nonfatal errors from the kernel passed thru udev events. There is a udev .rules file for invoking crash reporter. Currently it supports DRM error info logging. BUG=chrome-os-partner:6492 TEST=Run: "crash_reporter --udev=ACTION=change:KERNEL=card0:SUBSYSTEM=drm" Check that a card0-drm log file has been created under /var/spool/crash Change-Id: I05a1d508e5446988575b0c1924878b8e36ae46bb Signed-off-by: Simon Que Reviewed-on: https://gerrit.chromium.org/gerrit/10618 --- crash_reporter/99-crash-reporter.rules | 1 + crash_reporter/crash_collector.cc | 60 ++++++++++++++++++++++++- crash_reporter/crash_collector.h | 4 +- crash_reporter/crash_reporter.cc | 23 +++++++++- crash_reporter/crash_reporter_logs.conf | 6 ++- 5 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 crash_reporter/99-crash-reporter.rules diff --git a/crash_reporter/99-crash-reporter.rules b/crash_reporter/99-crash-reporter.rules new file mode 100644 index 000000000..25a1504c2 --- /dev/null +++ b/crash_reporter/99-crash-reporter.rules @@ -0,0 +1 @@ +ACTION=="change", SUBSYSTEM=="drm", KERNEL=="card0", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=KERNEL=card0:SUBSYSTEM=drm:ACTION=change" diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index fc7a52cdd..6046dc553 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,6 +14,7 @@ #include #include +#include #include "base/eintr_wrapper.h" #include "base/file_util.h" @@ -24,12 +25,16 @@ static const char kCollectChromeFile[] = "/mnt/stateful_partition/etc/collect_chrome_crashes"; +static const char kCollectUdevSignature[] = "crash_reporter-udev-collection"; static const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress"; +static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf"; static const char kDefaultUserName[] = "chronos"; static const char kLeaveCoreFile[] = "/root/.leave_core"; static const char kLsbRelease[] = "/etc/lsb-release"; static const char kShellPath[] = "/bin/sh"; static const char kSystemCrashPath[] = "/var/spool/crash"; +static const char kUdevExecName[] = "udev"; +static const char kUdevSignatureKey[] = "sig"; static const char kUserCrashPath[] = "/home/chronos/user/crash"; // Directory mode of the user crash spool directory. @@ -69,6 +74,59 @@ void CrashCollector::Initialize( is_feedback_allowed_function_ = is_feedback_allowed_function; } +bool CrashCollector::HandleUdevCrash(const std::string &udev_event) { + // Process the udev event string. + // The udev string should be formatted as follows: + // "ACTION=[action]:KERNEL=[name]:SUBSYSTEM=[subsystem]" + // The values don't have to be in any particular order. + + // First get all the key-value pairs. + std::vector > udev_event_keyval; + base::SplitStringIntoKeyValuePairs(udev_event, '=', ':', &udev_event_keyval); + std::vector >::const_iterator iter; + std::map udev_event_map; + for (iter = udev_event_keyval.begin(); + iter != udev_event_keyval.end(); + ++iter) { + udev_event_map[iter->first] = iter->second; + } + + // Construct the basename string for crash_reporter_logs.conf: + // "crash_reporter-udev-collection-[action]-[name]-[subsystem]" + // If a udev field is not provided, "" is used in its place, e.g.: + // "crash_reporter-udev-collection-[action]--[subsystem]" + // Hence, "" is used as a wildcard name string. + std::string basename = udev_event_map["ACTION"] + "-" + + udev_event_map["KERNEL"] + "-" + + udev_event_map["SUBSYSTEM"]; + std::string udev_log_name = std::string(kCollectUdevSignature) + '-' + + basename; + + // Make sure the crash directory exists, or create it if it doesn't. + FilePath crash_directory; + if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, NULL)) { + LOG(ERROR) << "Could not get crash directory."; + return false; + } + // Create the destination path. + std::string log_file_name = + FormatDumpBasename(basename, time(NULL), 0); + FilePath crash_path = GetCrashPath(crash_directory, log_file_name, "log"); + + // Handle the crash. + bool result = GetLogContents(FilePath(kDefaultLogConfig), udev_log_name, + crash_path); + if (!result) { + LOG(ERROR) << "Error reading udev log info " << udev_log_name; + return false; + } + + AddCrashMetaData(kUdevSignatureKey, kCollectUdevSignature); + WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"), + kUdevExecName, crash_path.value()); + return true; +} + int CrashCollector::WriteNewFile(const FilePath &filename, const char *data, int size) { diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 8759fc8f9..aa0041672 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -28,6 +28,8 @@ class CrashCollector { void Initialize(CountCrashFunction count_crash, IsFeedbackAllowedFunction is_metrics_allowed); + bool HandleUdevCrash(const std::string &udev_event); + protected: friend class CrashCollectorTest; FRIEND_TEST(CrashCollectorTest, CheckHasCapacityCorrectBasename); diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index ac3964bdf..263caf42e 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -1,14 +1,16 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include // for open #include +#include #include "base/file_util.h" #include "base/command_line.h" #include "base/logging.h" +#include "base/string_split.h" #include "base/string_util.h" #include "chromeos/syslog_logging.h" #include "crash-reporter/kernel_collector.h" @@ -25,6 +27,7 @@ DEFINE_string(generate_kernel_signature, "", DEFINE_bool(crash_test, false, "Crash test"); DEFINE_string(user, "", "User crash info (pid:signal:exec_name)"); DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); +DEFINE_string(udev, "", "Udev event description (type:device:subsystem)"); #pragma GCC diagnostic error "-Wstrict-aliasing" static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; @@ -160,6 +163,19 @@ static int HandleUserCrash(UserCollector *user_collector) { return 0; } +static int HandleUdevCrash(CrashCollector *udev_collector) { + // Handle a crash indicated by a udev event. + CHECK(!FLAGS_udev.empty()) << "--udev= must be set"; + + // Accumulate logs to help in diagnosing failures during user collection. + chromeos::LogToString(true); + bool handled = udev_collector->HandleUdevCrash(FLAGS_udev); + chromeos::LogToString(false); + if (!handled) + return 1; + return 0; +} + // Interactive/diagnostics mode for generating kernel crash signatures. static int GenerateKernelSignature(KernelCollector *kernel_collector) { std::string kcrash_contents; @@ -236,5 +252,10 @@ int main(int argc, char *argv[]) { return GenerateKernelSignature(&kernel_collector); } + if (!FLAGS_udev.empty()) { + CrashCollector udev_collector; + return HandleUdevCrash(&udev_collector); + } + return HandleUserCrash(&user_collector); } diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index c91d4d595..5650346ba 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -1,4 +1,4 @@ -# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can # be found in the LICENSE file. @@ -27,6 +27,10 @@ cros_installer:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log # the commands' arguments (i.e. "comm" instead of "command"). crash_reporter-user-collection:echo "===ps output==="; ps axw -o user,pid,%cpu,%mem,vsz,rss,tname,stat,start_time,bsdtime,comm | tail -c 25000; echo "===dmesg output==="; dmesg | tail -c 25000; echo "===meminfo==="; cat /proc/meminfo +# This rule is similar to the crash_reporter-user-collection rule, except it is +# run for kernel errors reported through udev events. +crash_reporter-udev-collection-change-card0-drm:for dri in /sys/kernel/debug/dri/*; do echo "===$dri/i915_error_state==="; cat $dri/i915_error_state; done + # The following rules are only for testing purposes. crash_log_test:echo hello world crash_log_recursion_test:sleep 1 && /usr/local/autotest/tests/crash_log_recursion_test From 57b261cf04f45d67b02f6bea8ad2c37734da86e0 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 11 Apr 2012 14:47:09 -0400 Subject: [PATCH 084/225] update to newer libbase BUG=chromium-os:25872 TEST=`emerge-x86-alex crash-reporter` works TEST=`cros_run_unit_tests --board=x86-alex -p crash-reporter` passed Change-Id: Iff428b1364f96533d2d2290272823c2c84c48933 Reviewed-on: https://gerrit.chromium.org/gerrit/20008 Commit-Ready: Mike Frysinger Reviewed-by: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/Makefile | 4 ++-- crash_reporter/crash_collector.cc | 1 + crash_reporter/crash_collector_test.cc | 3 ++- crash_reporter/crash_reporter.cc | 1 + crash_reporter/kernel_collector.cc | 3 ++- crash_reporter/kernel_collector_test.cc | 3 ++- crash_reporter/user_collector.cc | 3 ++- crash_reporter/user_collector_test.cc | 4 ++-- 8 files changed, 14 insertions(+), 8 deletions(-) diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index 9a25a8839..b8870a7d7 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -1,8 +1,8 @@ -# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -BASE_VER = 85268 +BASE_VER ?= 125070 PKG_CONFIG ?= pkg-config CRASH_REPORTER = crash_reporter diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 6046dc553..8b80f3715 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -21,6 +21,7 @@ #include "base/logging.h" #include "base/string_split.h" #include "base/string_util.h" +#include "base/stringprintf.h" #include "chromeos/process.h" static const char kCollectChromeFile[] = diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index d98e1af97..2635bbfcc 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,6 +6,7 @@ #include "base/file_util.h" #include "base/string_util.h" +#include "base/stringprintf.h" #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/crash_collector.h" diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 263caf42e..4fab0b7aa 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -12,6 +12,7 @@ #include "base/logging.h" #include "base/string_split.h" #include "base/string_util.h" +#include "base/stringprintf.h" #include "chromeos/syslog_logging.h" #include "crash-reporter/kernel_collector.h" #include "crash-reporter/unclean_shutdown_collector.h" diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 0895b4eb3..e15dc8553 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,6 +7,7 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" +#include "base/stringprintf.h" static const char kDefaultKernelStackSignature[] = "kernel-UnspecifiedStackSignature"; diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 873a5a7cd..59f435a2e 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,6 +6,7 @@ #include "base/file_util.h" #include "base/string_util.h" +#include "base/stringprintf.h" #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/kernel_collector.h" diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 28c5ce6c9..b060bd69d 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -19,6 +19,7 @@ #include "base/logging.h" #include "base/string_split.h" #include "base/string_util.h" +#include "base/stringprintf.h" #include "chromeos/process.h" #include "chromeos/syslog_logging.h" #include "gflags/gflags.h" diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 2d3ac0c76..8fcbaefd6 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,7 +8,7 @@ #include "base/file_util.h" #include "base/string_split.h" -#include "base/memory/scoped_temp_dir.h" +#include "base/scoped_temp_dir.h" #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/user_collector.h" From 87a495030d849b38b88464b99d7448c6bcea93ad Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Thu, 12 Apr 2012 15:24:22 -0700 Subject: [PATCH 085/225] crash-reporter: Fix unclean_shutdown_collector_test to work by itself The CantDisable test was failing because it couldn't properly fake the enabling of the crash collector -- which meant that the disable didn't actually fail. The reason was that the "test" directory was being removed during SetUp(). I also added/improved some error-checking that helped me track down various unittest failures. BUG=chromium-os:29395 TEST=Ran unittests Change-Id: I8cf50dbd0d5187b0028c8cf16ae10a4f68bb160f Reviewed-on: https://gerrit.chromium.org/gerrit/20129 Commit-Ready: Michael Krebs Tested-by: Michael Krebs Reviewed-by: Ben Chan --- crash_reporter/kernel_collector_test.cc | 10 ++++++++-- .../unclean_shutdown_collector_test.cc | 16 ++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 59f435a2e..ea18c6270 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -41,7 +41,11 @@ class KernelCollectorTest : public ::testing::Test { collector_.OverridePreservedDumpPath(test_kcrash_); test_kcrash_ = test_kcrash_.Append("dmesg-ramoops-0"); unlink(test_kcrash_.value().c_str()); - mkdir(kTestCrashDirectory, 0777); + if (mkdir(kTestCrashDirectory, 0777)) { + ASSERT_EQ(EEXIST, errno) + << "Error while creating directory '" << kTestCrashDirectory + << "': " << strerror(errno); + } chromeos::ClearLog(); } protected: @@ -257,7 +261,9 @@ TEST_F(KernelCollectorTest, CollectOK) { static const char kNamePrefix[] = "Stored kcrash to "; std::string log = chromeos::GetLog(); size_t pos = log.find(kNamePrefix); - ASSERT_NE(std::string::npos, pos); + ASSERT_NE(std::string::npos, pos) + << "Did not find string \"" << kNamePrefix << "\" in log: {\n" + << log << "}"; pos += strlen(kNamePrefix); std::string filename = log.substr(pos, std::string::npos); // Take the name up until \n diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc index 89b0ec9c6..8adb4d384 100644 --- a/crash_reporter/unclean_shutdown_collector_test.cc +++ b/crash_reporter/unclean_shutdown_collector_test.cc @@ -15,6 +15,7 @@ static int s_crashes = 0; static bool s_metrics = true; +static const char kTestDirectory[] = "test"; static const char kTestLowBattery[] = "test/low_battery"; static const char kTestSuspended[] = "test/suspended"; static const char kTestUnclean[] = "test/unclean"; @@ -34,7 +35,7 @@ class UncleanShutdownCollectorTest : public ::testing::Test { s_crashes = 0; collector_.Initialize(CountCrash, IsMetrics); - rmdir("test"); + rmdir(kTestDirectory); test_unclean_ = FilePath(kTestUnclean); collector_.unclean_shutdown_file_ = kTestUnclean; file_util::Delete(test_unclean_, true); @@ -60,7 +61,7 @@ TEST_F(UncleanShutdownCollectorTest, EnableWithoutParent) { } TEST_F(UncleanShutdownCollectorTest, EnableWithParent) { - mkdir("test", 0777); + mkdir(kTestDirectory, 0777); ASSERT_TRUE(collector_.Enable()); ASSERT_TRUE(file_util::PathExists(test_unclean_)); } @@ -121,8 +122,15 @@ TEST_F(UncleanShutdownCollectorTest, DisableWhenNotEnabled) { } TEST_F(UncleanShutdownCollectorTest, CantDisable) { - mkdir(kTestUnclean, 0700); - file_util::WriteFile(test_unclean_.Append("foo"), "", 0); + mkdir(kTestDirectory, 0700); + if (mkdir(kTestUnclean, 0700)) { + ASSERT_EQ(EEXIST, errno) + << "Error while creating directory '" << kTestUnclean + << "': " << strerror(errno); + } + ASSERT_EQ(0, file_util::WriteFile(test_unclean_.Append("foo"), "", 0)) + << "Error while creating empty file '" + << test_unclean_.Append("foo").value() << "': " << strerror(errno); ASSERT_FALSE(collector_.Disable()); rmdir(kTestUnclean); } From 7672bfb67e75d74745f6e655571b41dd83ca4046 Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Thu, 12 Apr 2012 16:20:51 -0700 Subject: [PATCH 086/225] crash-reporter: Convert Makefile over to using common.mk This will allow running of unittests on ARM, as well as cleaning up the make output some. BUG=chromium-os:27127 TEST=Ran unittests, logging_CrashSender, and logging_UserCrash Change-Id: Ide09a42132be0843146dde6687e4424352b6b866 Reviewed-on: https://gerrit.chromium.org/gerrit/20174 Tested-by: Michael Krebs Reviewed-by: Mike Frysinger Reviewed-by: Ben Chan Commit-Ready: Michael Krebs --- crash_reporter/Makefile | 79 ++-- crash_reporter/common.mk | 801 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 849 insertions(+), 31 deletions(-) create mode 100644 crash_reporter/common.mk diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index b8870a7d7..085b7def5 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -2,55 +2,72 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# TODO(mkrebs): This currently grabs a copy we made from +# src/platform/common-mk; we should instead share it. +include common.mk + BASE_VER ?= 125070 PKG_CONFIG ?= pkg-config -CRASH_REPORTER = crash_reporter -LIST_PROXIES_BIN = list_proxies -REPORTER_BINS = $(CRASH_REPORTER) $(LIST_PROXIES_BIN) CRASH_OBJS = \ crash_collector.o \ kernel_collector.o \ unclean_shutdown_collector.o \ user_collector.o -LIST_PROXIES_OBJS = list_proxies.o -LIST_PROXIES_PKGS = dbus-1 dbus-glib-1 -TEST_OBJS = $(CRASH_OBJS) -TEST_BINS = \ - crash_collector_test \ - kernel_collector_test \ - unclean_shutdown_collector_test \ - user_collector_test -PC_DEPS = glib-2.0 $(LIST_PROXIES_PKGS) libpcrecpp \ - libchrome-$(BASE_VER) libchromeos-$(BASE_VER) +PC_DEPS = glib-2.0 libpcrecpp libchrome-$(BASE_VER) libchromeos-$(BASE_VER) PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) PC_LIBS := $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) -COMMON_LIBS = -lgflags $(PC_LIBS) -REPORTER_LIBS = $(COMMON_LIBS) -lmetrics -LIST_PROXIES_LIBS = $(COMMON_LIBS) - -TEST_LIBS = $(COMMON_LIBS) -lgtest -lgmock CPPFLAGS += -I.. -I$(SYSROOT)/usr/include/google-breakpad $(PC_CFLAGS) +LDLIBS = -lgflags $(PC_LIBS) -CXXFLAGS += -Wall -Werror -all: $(REPORTER_BINS) +# -- Executable crash_reporter -- +# Disable error "format not a string literal, argument types not checked" for +# the StringPrintf() in KernelCollector::GetRamoopsRecordPath() because it's +# valid, but GNU apparently doesn't bother checking a const format string. +kernel_collector.o: CXXFLAGS += -Wno-format-nonliteral -$(CRASH_REPORTER): crash_reporter.o $(CRASH_OBJS) - $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ $(REPORTER_LIBS) -o $@ +CXX_BINARY(crash_reporter): LDLIBS += -lmetrics +CXX_BINARY(crash_reporter): crash_reporter.o $(CRASH_OBJS) +clean: CLEAN(crash_reporter) +all: CXX_BINARY(crash_reporter) -$(LIST_PROXIES_BIN): $(LIST_PROXIES_OBJS) - $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ $(LIST_PROXIES_LIBS) -o $@ -tests: $(TEST_BINS) +# -- Executable list_proxies -- +LIST_PROXIES_PKGS = dbus-1 dbus-glib-1 +LIST_PROXIES_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(LIST_PROXIES_PKGS)) +LIST_PROXIES_LIBS := $(shell $(PKG_CONFIG) --libs $(LIST_PROXIES_PKGS)) +CXX_BINARY(list_proxies): CPPFLAGS += $(LIST_PROXIES_CFLAGS) +CXX_BINARY(list_proxies): LDLIBS += $(LIST_PROXIES_LIBS) +CXX_BINARY(list_proxies): list_proxies.o +clean: CLEAN(list_proxies) +all: CXX_BINARY(list_proxies) -%_test: %_test.o $(TEST_OBJS) - $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ $(TEST_LIBS) -o $@ -.cc.o: - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ +# -- Unit Tests -- + +# Uncomment these to just run specific test(s). +#TEST_BINS += crash_collector_test +#TEST_BINS += kernel_collector_test +#TEST_BINS += unclean_shutdown_collector_test +#TEST_BINS += user_collector_test + + +# Define a template that defines the rules for a unittest. In case you want +# to add options, etc. for a particular test, the rule to build the test will +# be for "CXX_BINARY()". For example, you could add options with: +# CXX_BINARY(crash_collector_test): CXXFLAGS += -some-other-option +define TEST_RULES_template +CXX_BINARY($(1)): LDLIBS += -lgtest -lgmock +CXX_BINARY($(1)): $(1).o $(CRASH_OBJS) +clean: CLEAN($(1)) +tests: TEST(CXX_BINARY($(1))) +endef + +# If $(TEST_BINS) is not already set, assume any filename matching *_test.cc +# is a test. +TEST_BINS ?= $(patsubst $(SRC)/%.cc,%,$(wildcard $(SRC)/*_test.cc)) +$(foreach prog,$(TEST_BINS),$(eval $(call TEST_RULES_template,$(prog)))) -clean: - rm -rf *.o $(CRASH_BIN) $(LIST_PROXIES_BIN) $(TEST_BINS) diff --git a/crash_reporter/common.mk b/crash_reporter/common.mk new file mode 100644 index 000000000..5b36080d8 --- /dev/null +++ b/crash_reporter/common.mk @@ -0,0 +1,801 @@ +# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# If this file is part of another source distribution, it's license may be +# stored in LICENSE.makefile or LICENSE.common.mk. +# +# NOTE NOTE NOTE +# The authoritative common.mk is located in: +# http://git.chromium.org/gitweb/?p=chromiumos/platform/common-mk.git +# Please make all changes there, then copy into place in other repos. +# NOTE NOTE NOTE +# +# This file provides a common architecture for building C/C++ source trees. +# It uses recursive makefile inclusion to create a single make process which +# can be built in the source tree or with the build artifacts placed elsewhere. +# +# It is fully parallelizable for all targets, including static archives. +# +# To use: +# 1. Place common.mk in your top source level +# 2. In your top-level Makefile, place "include common.mk" at the top +# 3. In all subdirectories, create a 'module.mk' file that starts with: +# include common.mk +# And then contains the remainder of your targets. +# 4. All build targets should look like: +# relative/path/target: relative/path/obj.o +# +# See existing makefiles for rule examples. +# +# Exported macros: +# - cc_binary, cxx_binary provide standard compilation steps for binaries +# - cxx_library, cc_library provide standard compilation steps for +# shared objects. +# All of the above optionally take an argument for extra flags. +# - update_archive creates/updates a given .a target +# +# Instead of using the build macros, most users can just use wrapped targets: +# - CXX_BINARY, CC_BINARY, CC_STATIC_BINARY, CXX_STATIC_BINARY +# - CXX_LIBRARY, CC_LIBRARY, CC_STATIC_LIBRARY, CXX_STATIC_LIBRARY +# - E.g., CXX_BINARY(mahbinary): foo.o +# - object.depends targets may be used when a prerequisite is required for an +# object file. Because object files result in multiple build artifacts to +# handle PIC and PIE weirdness. E.g. +# foo.o.depends: generated/dbus.h +# - TEST(binary) or TEST(CXX_BINARY(binary)) may be used as a prerequisite +# for the tests target to trigger an automated test run. +# - CLEAN(file_or_dir) dependency can be added to 'clean'. +# +# If source code is being generated, rules will need to be registered for +# compiling the objects. This can be done by adding one of the following +# to the Makefile: +# - For C source files +# $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/gen_b.o,CC,c)) +# - For C++ source files +# $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/gen_b.o,CXX,cc)) +# +# Exported targets meant to have prerequisites added to: +# - all - Your desired targets should be given +# - tests - Any TEST(test_binary) targets should be given +# - FORCE - force the given target to run regardless of changes +# In most cases, using .PHONY is preferred. +# +# Possible command line variables: +# - COLOR=[0|1] to set ANSI color output (default: 1) +# - VERBOSE=[0|1] to hide/show commands (default: 0) +# - MODE=dbg to turn down optimizations (default: opt) +# - ARCH=[x86|arm|supported qemu name] (default: from portage or uname -m) +# - SPLITDEBUG=[0|1] splits debug info in target.debug (default: 0) +# If NOSTRIP=1, SPLITDEBUG will never strip the final emitted objects. +# - NOSTRIP=[0|1] determines if binaries are stripped. (default: 1) +# NOSTRIP=0 and MODE=opt will also drop -g from the CFLAGS. +# - VALGRIND=[0|1] runs tests under valgrind (default: 0) +# - OUT=/path/to/builddir puts all output in given path (default: $PWD) +# - VALGRIND_ARGS="" supplies extra memcheck arguments +# +# Per-target(-ish) variable: +# - NEEDS_ROOT=[0|1] allows a TEST() target to run with root. +# Default is 0 unless it is running under QEmu. +# - NEEDS_MOUNTS=[0|1] allows a TEST() target running on QEmu to get +# setup mounts in the $(SYSROOT) +# +# Caveats: +# - Directories or files with spaces in them DO NOT get along with GNU Make. +# If you need them, all uses of dir/notdir/etc will need to have magic +# wrappers. Proceed at risk to your own sanity. +# - External CXXFLAGS and CFLAGS should be passed via the environment since +# this file does not use 'override' to control them. +# - Our version of GNU Make doesn't seem to support the 'private' variable +# annotation, so you can't tag a variable private on a wrapping target. + +# Behavior configuration variables +SPLITDEBUG ?= 0 +NOSTRIP ?= 1 +VALGRIND ?= 0 +COLOR ?= 1 +VERBOSE ?= 0 +MODE ?= opt +ARCH ?= $(shell uname -m) +# TODO: profiling support not completed. +PROFILING ?= 0 +NEEDS_ROOT = 0 +NEEDS_MOUNTS = 0 + +# Put objects in a separate tree based on makefile locations +# This means you can build a tree without touching it: +# make -C $SRCDIR # will create ./build-$(MODE) +# Or +# make -C $SRCDIR OUT=$PWD +# This variable is extended on subdir calls and doesn't need to be re-called. +OUT ?= $(PWD)/ + +# Make OUT now so we can use realpath. +$(shell mkdir -p "$(OUT)") + +# TODO(wad) Relative paths are resolved against SRC and not the calling dir. +# Ensure a command-line supplied OUT has a slash +override OUT := $(realpath $(OUT))/ + +# SRC is not meant to be set by the end user, but during make call relocation. +# $(PWD) != $(CURDIR) all the time. +export SRC ?= $(CURDIR) + +# Re-start in the $(OUT) directory if we're not there. +# We may be invoked using -C or bare and we need to ensure behavior +# is consistent so we check both PWD vs OUT and PWD vs CURDIR. +override RELOCATE_BUILD := 0 +ifneq (${PWD}/,${OUT}) +override RELOCATE_BUILD := 1 +endif +# Make sure we're running with no builtin targets. They cause +# leakage and mayhem! +ifneq (${PWD},${CURDIR}) +override RELOCATE_BUILD := 1 +# If we're run from the build dir, don't let it get cleaned up later. +ifeq (${PWD}/,${OUT}) +$(shell touch "$(PWD)/.dont_delete_on_clean") +endif +endif # ifneq (${PWD},${CURDIR} + +# "Relocate" if we need to restart without implicit rules. +ifeq ($(subst r,,$(MAKEFLAGS)),$(MAKEFLAGS)) +override RELOCATE_BUILD := 1 +endif + +ifeq (${RELOCATE_BUILD},1) +# By default, silence build output. Reused below as well. +QUIET = @ +ifeq ($(VERBOSE),1) + QUIET= +endif + +# This target will override all targets, including prerequisites. To avoid +# calling $(MAKE) once per prereq on the given CMDGOAL, we guard it with a local +# variable. +RUN_ONCE := 0 +MAKECMDGOALS ?= all +# Keep the rules split as newer make does not allow them to be declared +# on the same line. But the way :: rules work, the _all here will also +# invoke the %:: rule while retaining "_all" as the default. +_all:: +%:: + $(if $(filter 0,$(RUN_ONCE)), \ + $(QUIET)mkdir -p "$(OUT)" && \ + cd $(OUT) && \ + $(MAKE) -r -I "$(SRC)" -f "$(CURDIR)/Makefile" \ + SRC="$(CURDIR)" OUT="$(OUT)" $(foreach g,$(MAKECMDGOALS),"$(g)"),) + $(eval RUN_ONCE := 1) +pass-to-subcall := 1 +endif + +ifeq ($(pass-to-subcall),) + +# Only call MODULE if we're in a submodule +MODULES_LIST := $(filter-out Makefile %.d,$(MAKEFILE_LIST)) +ifeq ($(words $(filter-out Makefile common.mk %.d $(SRC)/Makefile \ + $(SRC)/common.mk,$(MAKEFILE_LIST))),0) + +# All the top-level defines outside of module.mk. + +# +# Helper macros +# + +# Creates the actual archive with an index. +# The target $@ must end with .pic.a or .pie.a. +define update_archive + $(QUIET)mkdir -p "$(dir $(TARGET_OR_MEMBER))" + $(QUIET)# Create the archive in one step to avoid parallel use accessing it + $(QUIET)# before all the symbols are present. + @$(ECHO) "AR $(subst \ +$(SRC)/,,$(^:.o=$(suffix $(basename $(TARGET_OR_MEMBER))).o)) \ +-> $(subst $(SRC)/,,$(TARGET_OR_MEMBER))" + $(QUIET)$(AR) rcs $(TARGET_OR_MEMBER) \ + $(subst $(SRC)/,,$(^:.o=$(suffix $(basename $(TARGET_OR_MEMBER))).o)) +endef + +# Default compile from objects using pre-requisites but filters out +# subdirs and .d files. +define cc_binary + $(call COMPILE_BINARY_implementation,CC,$(CFLAGS) $(1),$(EXTRA_FLAGS)) +endef + +define cxx_binary + $(call COMPILE_BINARY_implementation,CXX,$(CXXFLAGS) $(1),$(EXTRA_FLAGS)) +endef + +# Default compile from objects using pre-requisites but filters out +# subdirs and .d files. +define cc_library + $(call COMPILE_LIBRARY_implementation,CC,$(CFLAGS) $(1),$(EXTRA_FLAGS)) +endef +define cxx_library + $(call COMPILE_LIBRARY_implementation,CXX,$(CXXFLAGS) $(1),$(EXTRA_FLAGS)) +endef + +# Deletes files silently if they exist. Meant for use in any local +# clean targets. +define silent_rm + $(if $(wildcard $(1)), + $(QUIET)($(ECHO) -n '$(COLOR_RED)CLEANFILE$(COLOR_RESET) ' && \ + $(ECHO) '$(subst $(OUT)/,,$(wildcard $(1)))' && \ + $(RM) $(1) 2>/dev/null) || true,) +endef +define silent_rmdir + $(if $(wildcard $(1)), + $(if $(wildcard $(1)/*), + $(QUIET)# $(1) not empty [$(wildcard $(1)/*)]. Not deleting., + $(QUIET)($(ECHO) -n '$(COLOR_RED)CLEANDIR$(COLOR_RESET) ' && \ + $(ECHO) '$(subst $(OUT)/,,$(wildcard $(1)))' && \ + $(RMDIR) $(1) 2>/dev/null) || true),) +endef + +# +# Default variable values +# + +OBJCOPY ?= objcopy +STRIP ?= strip +RMDIR ?= rmdir +# Only override CC and CXX if they are from make. +ifeq ($(origin CC), default) + CC = gcc +endif +ifeq ($(origin CXX), default) + CXX = g++ +endif +ifeq ($(origin RANLIB), default) + RANLIB = ranlib +endif +RANLIB ?= ranlib +ECHO = /bin/echo -e + +ifeq ($(PROFILING),1) + $(warning PROFILING=1 disables relocatable executables.) +endif + +# To update these from an including Makefile: +# CXXFLAGS += -mahflag # Append to the list +# CXXFLAGS := -mahflag $(CXXFLAGS) # Prepend to the list +# CXXFLAGS := $(filter-out badflag,$(CXXFLAGS)) # Filter out a value +# The same goes for CFLAGS. +# TODO(wad) Moving to -fvisibility=internal by default would be nice too. +COMMON_CFLAGS := -Wall -Werror -fstack-protector-strong -fno-strict-aliasing \ + -ggdb3 -Wa,--noexecstack -O1 -fvisibility=internal -Wformat=2 +CXXFLAGS += $(COMMON_CFLAGS) +CFLAGS += $(COMMON_CFLAGS) +CPPFLAGS += -D_FORTIFY_SOURCE=2 + +ifeq ($(PROFILING),1) + CFLAGS := -pg + CXXFLAGS := -pg +endif + +ifeq ($(MODE),opt) + # Up the optimizations. + CFLAGS := $(filter-out -O1,$(CFLAGS)) -O2 + CXXFLAGS := $(filter-out -O1,$(CXXFLAGS)) -O2 + # Only drop -g* if symbols aren't desired. + ifeq ($(NOSTRIP),0) + # TODO: do we want -fomit-frame-pointer on x86? + CFLAGS := $(filter-out -ggdb3,$(CFLAGS)) + CXXFLAGS := $(filter-out -ggdb3,$(CXXFLAGS)) + endif +endif + +LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,noexecstack -Wl,-z,now + +# Fancy helpers for color if a prompt is defined +ifeq ($(COLOR),1) +COLOR_RESET = \x1b[0m +COLOR_GREEN = \x1b[32;01m +COLOR_RED = \x1b[31;01m +COLOR_YELLOW = \x1b[33;01m +endif + +# By default, silence build output. +QUIET = @ +ifeq ($(VERBOSE),1) + QUIET= +endif + +# +# Implementation macros for compile helpers above +# + +# Useful for dealing with pie-broken toolchains. +# Call make with PIE=0 to disable default PIE use. +OBJ_PIE_FLAG = -fPIE +COMPILE_PIE_FLAG = -pie +ifeq ($(PIE),0) + OBJ_PIE_FLAG = + COMPILE_PIE_FLAG = +endif + +# Favor member targets first for CXX_BINARY(%) magic. +# And strip out nested members if possible. +LP := ( +RP := ) +TARGET_OR_MEMBER = $(lastword $(subst $(LP), ,$(subst $(RP),,$(or $%,$@)))) + +# Default compile from objects using pre-requisites but filters out +# all non-.o files. +define COMPILE_BINARY_implementation + @$(ECHO) "LD$(1) $(subst $(PWD)/,,$(TARGET_OR_MEMBER))" + $(QUIET)mkdir -p "$(dir $(TARGET_OR_MEMBER))" + $(QUIET)$($(1)) $(COMPILE_PIE_FLAGS) -o $(TARGET_OR_MEMBER) \ + $(2) $(LDFLAGS) \ + $(filter %.o %.a,$(^:.o=.pie.o)) \ + $(foreach so,$(filter %.so,$^),-L$(dir $(so)) \ + -l$(patsubst lib%,%,$(basename $(notdir $(so))))) \ + $(LDLIBS) + $(call conditional_strip) + @$(ECHO) -n "BIN " + @$(ECHO) "$(COLOR_GREEN)$(subst $(PWD)/,,$(TARGET_OR_MEMBER))$(COLOR_RESET)" + @$(ECHO) " $(COLOR_YELLOW)-----$(COLOR_RESET)" +endef + +# TODO: add version support extracted from PV environment variable +#ifeq ($(PV),9999) +#$(warning PV=$(PV). If shared object versions matter, please force PV=.) +#endif +# Then add -Wl,-soname,$@.$(PV) ? + +# Default compile from objects using pre-requisites but filters out +# all non-.o values. (Remember to add -L$(OUT) -llib) +COMMA := , +define COMPILE_LIBRARY_implementation + @$(ECHO) "SHARED$(1) $(subst $(PWD)/,,$(TARGET_OR_MEMBER))" + $(QUIET)mkdir -p "$(dir $(TARGET_OR_MEMBER))" + $(QUIET)$($(1)) -shared -Wl,-E -o $(TARGET_OR_MEMBER) \ + $(2) $(LDFLAGS) \ + $(if $(filter %.a,$^),-Wl$(COMMA)--whole-archive,) \ + $(filter %.o ,$(^:.o=.pic.o)) \ + $(foreach a,$(filter %.a,$^),-L$(dir $(a)) \ + -l$(patsubst lib%,%,$(basename $(notdir $(a))))) \ + $(foreach so,$(filter %.so,$^),-L$(dir $(so)) \ + -l$(patsubst lib%,%,$(basename $(notdir $(so))))) \ + $(LDLIBS) + $(call conditional_strip) + @$(ECHO) -n "LIB $(COLOR_GREEN)" + @$(ECHO) "$(subst $(PWD)/,,$(TARGET_OR_MEMBER))$(COLOR_RESET)" + @$(ECHO) " $(COLOR_YELLOW)-----$(COLOR_RESET)" +endef + +define conditional_strip + $(if $(filter 0,$(NOSTRIP)),$(call strip_artifact)) +endef + +define strip_artifact + @$(ECHO) "STRIP $(subst $(OUT)/,,$(TARGET_OR_MEMBER))" + $(if $(filter 1,$(SPLITDEBUG)), @$(ECHO) -n "DEBUG "; \ + $(ECHO) "$(COLOR_YELLOW)\ +$(subst $(OUT)/,,$(TARGET_OR_MEMBER)).debug$(COLOR_RESET)") + $(if $(filter 1,$(SPLITDEBUG)), \ + $(QUIET)$(OBJCOPY) --only-keep-debug "$(TARGET_OR_MEMBER)" \ + "$(TARGET_OR_MEMBER).debug") + $(if $(filter-out dbg,$(MODE)),$(QUIET)$(STRIP) --strip-unneeded \ + "$(TARGET_OR_MEMBER)",) +endef + +# +# Global pattern rules +# + +# Below, the archive member syntax is abused to create fancier +# syntactic sugar for recipe authors that avoids needed to know +# subcall options. The downside is that make attempts to look +# into the phony archives for timestamps. This will cause the final +# target to be rebuilt/linked on _every_ call to make even when nothing +# has changed. Until a better way presents itself, we have helpers that +# do the stat check on make's behalf. Dodgy but simple. +define old_or_no_timestamp + $(if $(realpath $%),,$(1)) + $(if $(shell find $^ -cnewer "$%" 2>/dev/null),$(1)) +endef + +define check_deps + $(if $(filter 0,$(words $^)),\ + $(error Missing dependencies or declaration of $@($%)),) +endef + +# Build a cxx target magically +CXX_BINARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cxx_binary)) +clean: CLEAN(CXX_BINARY*) + +CC_BINARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cc_binary)) +clean: CLEAN(CC_BINARY*) + +CXX_STATIC_BINARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cxx_binary,-static)) +clean: CLEAN(CXX_STATIC_BINARY*) + +CC_STATIC_BINARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cc_binary,-static)) +clean: CLEAN(CC_STATIC_BINARY*) + +CXX_LIBRARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cxx_library)) +clean: CLEAN(CXX_LIBRARY*) + +CXX_LIBARY(%): + $(error Typo alert! LIBARY != LIBRARY) + +CC_LIBRARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cc_library)) +clean: CLEAN(CC_LIBRARY*) + +CC_LIBARY(%): + $(error Typo alert! LIBARY != LIBRARY) + +CXX_STATIC_LIBRARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call update_archive)) +clean: CLEAN(CXX_STATIC_LIBRARY*) + +CXX_STATIC_LIBARY(%): + $(error Typo alert! LIBARY != LIBRARY) + +CC_STATIC_LIBRARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call update_archive)) +clean: CLEAN(CC_STATIC_LIBRARY*) + +CC_STATIC_LIBARY(%): + $(error Typo alert! LIBARY != LIBRARY) + + +TEST(%): % qemu_chroot_install + $(call TEST_implementation) +.PHONY: TEST + +# multiple targets with a wildcard need to share an directory. +# Don't use this directly it just makes sure the directory is removed _after_ +# the files are. +CLEANFILE(%): + $(call silent_rm,$(TARGET_OR_MEMBER)) +.PHONY: CLEANFILE + +CLEAN(%): CLEANFILE(%) + $(QUIET)# CLEAN($%) meta-target called + $(if $(filter-out $(PWD)/,$(dir $(abspath $(TARGET_OR_MEMBER)))), \ + $(call silent_rmdir,$(dir $(abspath $(TARGET_OR_MEMBER)))),\ + $(QUIET)# Not deleting $(dir $(abspath $(TARGET_OR_MEMBER))) yet.) +.PHONY: CLEAN + +# +# Top-level objects and pattern rules +# + +# All objects for .c files at the top level +C_OBJECTS = $(patsubst $(SRC)/%.c,%.o,$(wildcard $(SRC)/*.c)) + + +# All objects for .cxx files at the top level +CXX_OBJECTS = $(patsubst $(SRC)/%.cc,%.o,$(wildcard $(SRC)/*.cc)) + +# Note, the catch-all pattern rules don't work in subdirectories because +# we're building from the $(OUT) directory. At the top-level (here) they will +# work, but we go ahead and match using the module form. Then we can place a +# generic pattern rule to capture leakage from the main Makefile. (Later in the +# file.) +# +# The reason target specific pattern rules work well for modules, +# MODULE_C_OBJECTS, is because it scopes the behavior to the given target which +# ensures we get a relative directory offset from $(OUT) which otherwise would +# not match without further magic on a per-subdirectory basis. + +# Creates object file rules. Call with eval. +# $(1) list of .o files +# $(2) source type (CC or CXX) +# $(3) source suffix (cc or c) +# $(4) compiler flag name (CFLAGS or CXXFLAGS) +# $(5) source dir: _only_ if $(SRC). Leave blank for obj tree. +define add_object_rules +$(patsubst %.o,%.pie.o,$(1)): %.pie.o: $(5)%.$(3) %.o.depends + $$(QUIET)mkdir -p "$$(dir $$@)" + $$(call OBJECT_PATTERN_implementation,$(2),\ + $$(basename $$@),$$($(4)) $$(CPPFLAGS) $$(OBJ_PIE_FLAG)) + +$(patsubst %.o,%.pic.o,$(1)): %.pic.o: $(5)%.$(3) %.o.depends + $$(QUIET)mkdir -p "$$(dir $$@)" + $$(call OBJECT_PATTERN_implementation,$(2),\ + $$(basename $$@),$$($(4)) $$(CPPFLAGS) -fPIC) + +# Placeholder for depends +$(patsubst %.o,%.o.depends,$(1)): + $$(QUIET)mkdir -p "$$(dir $$@)" + $$(QUIET)touch "$$@" + +$(1): %.o: %.pic.o %.pie.o + $$(QUIET)mkdir -p "$$(dir $$@)" + $$(QUIET)touch "$$@" +endef + +define OBJECT_PATTERN_implementation + @$(ECHO) "$(1) $(subst $(SRC)/,,$<) -> $(2).o" + $(QUIET)mkdir -p "$(dir $(2))" + $(QUIET)$($(1)) -c -MD -MF $(2).d $(3) -o $(2).o $< + $(QUIET)# Wrap all the deps in $(wildcard) so a missing header + $(QUIET)# won't cause weirdness. First we remove newlines and \, + $(QUIET)# then wrap it. + $(QUIET)sed -i -e :j -e '$$!N;s|\\\s*\n| |;tj' \ + -e 's|^\(.*\s*:\s*\)\(.*\)$$|\1 $$\(wildcard \2\)|' $(2).d +endef + +# Now actually register handlers for C(XX)_OBJECTS. +$(eval $(call add_object_rules,$(C_OBJECTS),CC,c,CFLAGS,$(SRC)/)) +$(eval $(call add_object_rules,$(CXX_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/)) + +# Disable default pattern rules to help avoid leakage. +# These may already be handled by '-r', but let's keep it to be safe. +%: %.o ; +%.a: %.o ; +%.o: %.c ; +%.o: %.cc ; + +# NOTE: A specific rule for archive objects is avoided because parallel +# update of the archive causes build flakiness. +# Instead, just make the objects the prerequisites and use update_archive +# To use the foo.a(obj.o) functionality, targets would need to specify the +# explicit object they expect on the prerequisite line. + +# +# Architecture detection and QEMU wrapping +# + +HOST_ARCH ?= $(shell uname -m) +override ARCH := $(strip $(ARCH)) +override HOST_ARCH := $(strip $(HOST_ARCH)) +# emake will supply "x86" or "arm" for ARCH, but +# if uname -m runs and you get x86_64, then this subst +# will break. +ifeq ($(subst x86,i386,$(ARCH)),i386) + QEMU_ARCH := $(subst x86,i386,$(ARCH)) # x86 -> i386 +else ifeq ($(subst amd64,x86_64,$(ARCH)),x86_64) + QEMU_ARCH := $(subst amd64,x86_64,$(ARCH)) # amd64 -> x86_64 +else + QEMU_ARCH = $(ARCH) +endif +override QEMU_ARCH := $(strip $(QEMU_ARCH)) + +# If we're cross-compiling, try to use qemu for running the tests. +ifneq ($(QEMU_ARCH),$(HOST_ARCH)) + ifeq ($(SYSROOT),) + $(info SYSROOT not defined. qemu-based testing disabled) + else + # A SYSROOT is assumed for QEmu use. + USE_QEMU ?= 1 + + # Allow 64-bit hosts to run 32-bit without qemu. + ifeq ($(HOST_ARCH),x86_64) + ifeq ($(QEMU_ARCH),i386) + USE_QEMU = 0 + endif + endif + endif +else + USE_QEMU ?= 0 +endif + +SYSROOT_OUT = $(OUT) +ifneq ($(SYSROOT),) + SYSROOT_OUT = $(subst $(SYSROOT),,$(OUT)) +else + # Default to / when all the empty-sysroot logic is done. + SYSROOT = / +endif + + +# +# Output full configuration at top level +# + +# Don't show on clean +ifneq ($(MAKECMDGOALS),clean) + $(info build configuration:) + $(info - OUT=$(OUT)) + $(info - SRC=$(SRC)) + $(info - MODE=$(MODE)) + $(info - SPLITDEBUG=$(SPLITDEBUG)) + $(info - NOSTRIP=$(NOSTRIP)) + $(info - VALGRIND=$(VALGRIND)) + $(info - COLOR=$(COLOR)) + $(info - ARCH=$(ARCH)) + $(info - QEMU_ARCH=$(QEMU_ARCH)) + $(info - SYSROOT=$(SYSROOT)) + $(info ) +endif + +# +# Standard targets with detection for when they are improperly configured. +# + +# all does not include tests by default +all: + $(QUIET)(test -z "$^" && \ + $(ECHO) "You must add your targets as 'all' prerequisites") || true + $(QUIET)test -n "$^" + +# Builds and runs tests for the target arch +# Run them in parallel +tests: +.PHONY: tests + +qemu_clean: + $(call if_qemu,$(call silent_rm,$(OUT)/qemu-$(QEMU_ARCH))) + +qemu_chroot_install: +ifeq ($(USE_QEMU),1) + $(QUIET)$(ECHO) "QEMU Preparing qemu-$(QEMU_ARCH)" + $(QUIET)cp -fu /usr/bin/qemu-$(QEMU_ARCH) $(OUT)/qemu-$(QEMU_ARCH) + $(QUIET)chmod a+rx $(OUT)/qemu-$(QEMU_ARCH) +endif +.PHONY: qemu_clean qemu_chroot_install + +# TODO(wad) Move to -L $(SYSROOT) and fakechroot when qemu-user +# doesn't hang traversing /proc from SYSROOT. +QEMU_CMD = +ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),sudo , ) +MOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) mount, \#) +UMOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) umount, \#) +QEMU_LDPATH = $(SYSROOT_LDPATH):/lib64:/lib:/usr/lib64:/usr/lib +ROOT_CMD_LDPATH = $(SYSROOT_LDPATH):$(SYSROOT)/lib64: +ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/lib:$(SYSROOT)/usr/lib64: +ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/usr/lib +ifeq ($(USE_QEMU),1) + export QEMU_CMD = \ + sudo chroot $(SYSROOT) $(SYSROOT_OUT)qemu-$(QEMU_ARCH) \ + -drop-ld-preload \ + -E LD_LIBRARY_PATH="$(QEMU_LDPATH):$(patsubst $(OUT),,$(LD_DIRS))" \ + -E HOME="$(HOME)" -- + # USE_QEMU conditional function + define if_qemu + $(1) + endef +else + ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),sudo, ) \ + LD_LIBRARY_PATH="$(ROOT_CMD_LDPATH):$(LD_DIRS)" + define if_qemu + $(2) + endef +endif + +VALGRIND_CMD = +ifeq ($(VALGRIND),1) + VALGRIND_CMD = /usr/bin/valgrind --tool=memcheck $(VALGRIND_ARGS) -- +endif + +define TEST_implementation + $(QUIET)$(call TEST_setup) + $(QUIET)$(call TEST_run) + $(QUIET)$(call TEST_teardown) + $(QUIET)exit $$(cat $(OUT)$(TARGET_OR_MEMBER).status.test) +endef + +define TEST_setup + @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " + @$(ECHO) "[$(COLOR_YELLOW)SETUP$(COLOR_RESET)]" + $(QUIET)# Setup a target-specific results file + $(QUIET)(echo 1 > $(OUT)$(TARGET_OR_MEMBER).status.test) + $(QUIET)(echo > $(OUT)$(TARGET_OR_MEMBER).cleanup.test) + $(QUIET)# No setup if we are not using QEMU + $(QUIET)# TODO(wad) this is racy until we use a vfs namespace + $(call if_qemu,\ + $(QUIET)sudo mkdir -p "$(SYSROOT)/proc" "$(SYSROOT)/dev") + $(call if_qemu,\ + $(QUIET)$(MOUNT_CMD) --bind /proc "$(SYSROOT)/proc") + $(call if_qemu,\ + $(QUIET)$(MOUNT_CMD) --bind /dev "$(SYSROOT)/dev") + $(call if_qemu,\ + $(QUIET)(echo "$(UMOUNT_CMD) -l '$(SYSROOT)/proc'" \ + >> "$(OUT)$(TARGET_OR_MEMBER).cleanup.test")) + $(call if_qemu,\ + $(QUIET)(echo "$(UMOUNT_CMD) -l '$(SYSROOT)/dev'" \ + >> "$(OUT)$(TARGET_OR_MEMBER).cleanup.test")) +endef + +define TEST_teardown + @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " + @$(ECHO) "[$(COLOR_YELLOW)TEARDOWN$(COLOR_RESET)]" + $(call if_qemu, $(QUIET)$(SHELL) "$(OUT)$(TARGET_OR_MEMBER).cleanup.test") +endef + +# Use GTEST_ARGS.[arch] if defined. +override GTEST_ARGS.real = \ + $(call if_qemu,$(GTEST_ARGS.qemu.$(QEMU_ARCH)),$(GTEST_ARGS.host.$(HOST_ARCH))) + +define TEST_run + @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " + @$(ECHO) "[$(COLOR_GREEN)RUN$(COLOR_RESET)]" + $(QUIET)(echo 1 > "$(OUT)$(TARGET_OR_MEMBER).status.test") + -($(ROOT_CMD) $(QEMU_CMD) $(VALGRIND_CMD) \ + "$(strip $(call if_qemu, $(SYSROOT_OUT),$(OUT))$(TARGET_OR_MEMBER))" \ + $(if $(filter-out 0,$(words $(GTEST_ARGS.real))),$(GTEST_ARGS.real),\ + $(GTEST_ARGS)) && \ + echo 0 > "$(OUT)$(TARGET_OR_MEMBER).status.test") +endef + +# Recursive list reversal so that we get RMDIR_ON_CLEAN in reverse order. +define reverse +$(if $(1),$(call reverse,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1)) +endef + +clean: qemu_clean +clean: CLEAN($(OUT)*.d) CLEAN($(OUT)*.o) CLEAN($(OUT)*.debug) +clean: CLEAN($(OUT)*.test) CLEAN($(OUT)*.depends) + +clean: + $(QUIET)# Always delete the containing directory last. + $(call silent_rmdir,$(OUT)) + +FORCE: ; +# Empty rule for use when no special targets are needed, like large_tests +NONE: + +.PHONY: clean NONE valgrind NONE +.DEFAULT_GOAL := all +# Don't let make blow away "intermediates" +.PRECIOUS: %.pic.o %.pie.o %.a %.pic.a %.pie.a %.test + +# Start accruing build info +OUT_DIRS = $(OUT) +LD_DIRS = $(OUT) +SRC_DIRS = $(SRC) + +include $(wildcard $(OUT)*.d) +SUBMODULE_DIRS = $(wildcard $(SRC)/*/module.mk) +include $(SUBMODULE_DIRS) + + +else ## In duplicate inclusions of common.mk + +# Get the current inclusion directory without a trailing slash +MODULE := $(patsubst %/,%, \ + $(dir $(lastword $(filter-out %common.mk,$(MAKEFILE_LIST))))) +MODULE := $(subst $(SRC)/,,$(MODULE)) +MODULE_NAME := $(subst /,_,$(MODULE)) +#VPATH := $(MODULE):$(VPATH) + + +# Depth first +$(eval OUT_DIRS += $(OUT)$(MODULE)) +$(eval SRC_DIRS += $(OUT)$(MODULE)) +$(eval LD_DIRS := $(LD_DIRS):$(OUT)$(MODULE)) + +# Add the defaults from this dir to rm_clean +clean: CLEAN($(OUT)$(MODULE)/*.d) CLEAN($(OUT)$(MODULE)/*.o) +clean: CLEAN($(OUT)$(MODULE)/*.debug) CLEAN($(OUT)$(MODULE)/*.test) +clean: CLEAN($(OUT)$(MODULE)/*.depends) + +$(info + submodule: $(MODULE_NAME)) +# We must eval otherwise they may be dropped. +MODULE_C_OBJECTS = $(patsubst $(SRC)/$(MODULE)/%.c,$(MODULE)/%.o,\ + $(wildcard $(SRC)/$(MODULE)/*.c)) +$(eval $(MODULE_NAME)_C_OBJECTS ?= $(MODULE_C_OBJECTS)) +MODULE_CXX_OBJECTS = $(patsubst $(SRC)/$(MODULE)/%.cc,$(MODULE)/%.o,\ + $(wildcard $(SRC)/$(MODULE)/*.cc)) +$(eval $(MODULE_NAME)_CXX_OBJECTS ?= $(MODULE_CXX_OBJECTS)) + +# Note, $(MODULE) is implicit in the path to the %.c. +# See $(C_OBJECTS) for more details. +# Register rules for the module objects. +$(eval $(call add_object_rules,$(MODULE_C_OBJECTS),CC,c,CFLAGS,$(SRC)/)) +$(eval $(call add_object_rules,$(MODULE_CXX_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/)) + +# Continue recursive inclusion of module.mk files +SUBMODULE_DIRS = $(wildcard $(SRC)/$(MODULE)/*/module.mk) +include $(wildcard $(OUT)$(MODULE)/*.d) +include $(SUBMODULE_DIRS) + +endif +endif ## pass-to-subcall wrapper for relocating the call directory From 1e09a84aafcadfcc6dc453f0a4791e50317e569a Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Wed, 18 Apr 2012 12:34:13 -0700 Subject: [PATCH 087/225] crash-reporter: Move disabling of GCC warning into source file Rather than disable the "format not a string literal, argument types not checked" warning for the entire kernel_collector.cc file, only disable it for the code in question. BUG=chromium-os:27127 TEST=Ran unittests Change-Id: Id85e7945779cd5d1b6a3b68cfdedae53b3f22c74 Reviewed-on: https://gerrit.chromium.org/gerrit/20484 Reviewed-by: Ben Chan Commit-Ready: Michael Krebs Tested-by: Michael Krebs --- crash_reporter/Makefile | 5 +---- crash_reporter/kernel_collector.cc | 6 ++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index 085b7def5..d123a806e 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -24,10 +24,6 @@ LDLIBS = -lgflags $(PC_LIBS) # -- Executable crash_reporter -- -# Disable error "format not a string literal, argument types not checked" for -# the StringPrintf() in KernelCollector::GetRamoopsRecordPath() because it's -# valid, but GNU apparently doesn't bother checking a const format string. -kernel_collector.o: CXXFLAGS += -Wno-format-nonliteral CXX_BINARY(crash_reporter): LDLIBS += -lmetrics CXX_BINARY(crash_reporter): crash_reporter.o $(CRASH_OBJS) @@ -36,6 +32,7 @@ all: CXX_BINARY(crash_reporter) # -- Executable list_proxies -- + LIST_PROXIES_PKGS = dbus-1 dbus-glib-1 LIST_PROXIES_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(LIST_PROXIES_PKGS)) LIST_PROXIES_LIBS := $(shell $(PKG_CONFIG) --libs $(LIST_PROXIES_PKGS)) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index e15dc8553..fc8500e8b 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -96,7 +96,13 @@ bool KernelCollector::ReadRecordToString(std::string *contents, void KernelCollector::GetRamoopsRecordPath(FilePath *path, size_t record) { + // Disable error "format not a string literal, argument types not checked" + // because this is valid, but GNU apparently doesn't bother checking a const + // format string. + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wformat-nonliteral" *path = ramoops_dump_path_.Append(StringPrintf(kDumpFormat, record)); + #pragma GCC diagnostic pop } bool KernelCollector::LoadParameters() { From ee173fbe36bcd175d185ad89e553c4abbf7be93e Mon Sep 17 00:00:00 2001 From: Sergiu Iordache Date: Fri, 5 Aug 2011 15:32:15 -0700 Subject: [PATCH 088/225] crash-reporter: Re-enable CollectBadDirectory unittest The KernelCollectorTest.CollectBadDirectory unittest was first re-enabled by sergiu@ in https://gerrit.chromium.org/gerrit/5433, but it broke the buildbots and had to be re-disabled. So I'm trying again, but this time I have the power of trybots! I also added code to output the log if it fails again. BUG=chromium-os:18622 TEST=Ran unittests. Ran trybots on: alex-paladin, amd64-generic-paladin, arm-tegra2-paladin, lumpy-paladin, stumpy-paladin. Change-Id: Ib7fa1c1a32e9c198db9bdae8f933c9dd9fc3381a Reviewed-on: https://gerrit.chromium.org/gerrit/20667 Tested-by: Michael Krebs Reviewed-by: Ben Chan Commit-Ready: Michael Krebs --- crash_reporter/kernel_collector_test.cc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index ea18c6270..ee96f058e 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -20,6 +20,7 @@ static const char kTestKCrash[] = "test/kcrash"; static const char kTestCrashDirectory[] = "test/crash_directory"; using chromeos::FindLog; +using chromeos::GetLog; void CountCrash() { ++s_crashes; @@ -230,14 +231,14 @@ TEST_F(KernelCollectorTest, CollectNoCrash) { ASSERT_EQ(0, s_crashes); } -// Disabled for crosbug.com/18622 -//TEST_F(KernelCollectorTest, CollectBadDirectory) { -// WriteStringToFile(test_kcrash_, "====1.1\nsomething"); -// ASSERT_TRUE(collector_.Collect()); -// ASSERT_TRUE(FindLog( -// "Unable to create appropriate crash directory")); -// ASSERT_EQ(1, s_crashes); -//} +TEST_F(KernelCollectorTest, CollectBadDirectory) { + WriteStringToFile(test_kcrash_, "====1.1\nsomething"); + ASSERT_TRUE(collector_.Collect()); + ASSERT_TRUE(FindLog("Unable to create appropriate crash directory")) + << "Did not find expected error string in log: {\n" + << GetLog() << "}"; + ASSERT_EQ(1, s_crashes); +} void KernelCollectorTest::SetUpSuccessfulCollect() { collector_.ForceCrashDirectory(kTestCrashDirectory); From b8ff76914d8dfa4f09533ad769e6b7ec377fbc77 Mon Sep 17 00:00:00 2001 From: Simon Que Date: Mon, 30 Apr 2012 15:41:10 -0700 Subject: [PATCH 089/225] crash collector: compress udev crash logs BUG=chrome-os-partner:6492 TEST=Run: "crash_reporter --udev=ACTION=change:KERNEL=card0:SUBSYSTEM=drm" Check that a card0-drm log.gz file has been created under /var/spool/crash Check that the corresponding .meta file points to the log.gz file. Change-Id: Id1ecc82b9fac118f8bf1e5edc40c3a2f61abd4c4 Signed-off-by: Simon Que Reviewed-on: https://gerrit.chromium.org/gerrit/21495 Reviewed-by: Ben Chan --- crash_reporter/crash_collector.cc | 13 +++++++++++++ crash_reporter/crash_collector.h | 1 + 2 files changed, 14 insertions(+) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 8b80f3715..aaee3cb59 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -37,6 +37,7 @@ static const char kSystemCrashPath[] = "/var/spool/crash"; static const char kUdevExecName[] = "udev"; static const char kUdevSignatureKey[] = "sig"; static const char kUserCrashPath[] = "/home/chronos/user/crash"; +static const char kGzipPath[] = "/bin/gzip"; // Directory mode of the user crash spool directory. static const mode_t kUserCrashPathMode = 0755; @@ -122,6 +123,18 @@ bool CrashCollector::HandleUdevCrash(const std::string &udev_event) { return false; } + // Compress the output using gzip. + chromeos::ProcessImpl gzip_process; + gzip_process.AddArg(kGzipPath); + gzip_process.AddArg(crash_path.value()); + int process_result = gzip_process.Run(); + FilePath crash_path_zipped = FilePath(crash_path.value() + ".gz"); + // If the zip file was not created, use the uncompressed file. + if (process_result != 0 || !file_util::PathExists(crash_path_zipped)) + LOG(ERROR) << "Could not create zip file " << crash_path_zipped.value(); + else + crash_path = crash_path_zipped; + AddCrashMetaData(kUdevSignatureKey, kCollectUdevSignature); WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"), kUdevExecName, crash_path.value()); diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index aa0041672..77e21214c 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -28,6 +28,7 @@ class CrashCollector { void Initialize(CountCrashFunction count_crash, IsFeedbackAllowedFunction is_metrics_allowed); + // TODO(crosbug.com/30268): refactor into separate class. bool HandleUdevCrash(const std::string &udev_event); protected: From 6f03549d97555810c8e190af3439b4e7c7bad10f Mon Sep 17 00:00:00 2001 From: Simon Que Date: Wed, 27 Jun 2012 17:28:14 -0700 Subject: [PATCH 090/225] Put send metrics code into common function SendEnumToUMA() and SendCrashToUMA() are being called from three functions in crash_reporter.cc. This patch puts the three calls plus a repeated TODO into one helper function that's called by the three existing functions. BUG=chromium-os:11163,chromium-os:30268 TEST=emerge crash-reporter Change-Id: I703d93e3a072faa0264a220a69df2203af100c57 Signed-off-by: Simon Que Reviewed-on: https://gerrit.chromium.org/gerrit/26291 Reviewed-by: Ben Chan --- crash_reporter/crash_reporter.cc | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 4fab0b7aa..e563b45ea 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -59,31 +59,23 @@ static bool TouchFile(const FilePath &file_path) { return file_util::WriteFile(file_path, "", 0) == 0; } -static void CountKernelCrash() { +static void SendCrashMetrics(CrashKinds type, const char* name) { // TODO(kmixter): We can remove this histogram as part of // crosbug.com/11163. - s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), - kCrashKindKernel, - kCrashKindMax); - s_metrics_lib.SendCrashToUMA("kernel"); + s_metrics_lib.SendEnumToUMA(kCrashCounterHistogram, type, kCrashKindMax); + s_metrics_lib.SendCrashToUMA(name); +} + +static void CountKernelCrash() { + SendCrashMetrics(kCrashKindKernel, "kernel"); } static void CountUncleanShutdown() { - // TODO(kmixter): We can remove this histogram as part of - // crosbug.com/11163. - s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), - kCrashKindUncleanShutdown, - kCrashKindMax); - s_metrics_lib.SendCrashToUMA("uncleanshutdown"); + SendCrashMetrics(kCrashKindUncleanShutdown, "uncleanshutdown"); } static void CountUserCrash() { - // TODO(kmixter): We can remove this histogram as part of - // crosbug.com/11163. - s_metrics_lib.SendEnumToUMA(std::string(kCrashCounterHistogram), - kCrashKindUser, - kCrashKindMax); - s_metrics_lib.SendCrashToUMA("user"); + SendCrashMetrics(kCrashKindUser, "user"); std::string command = StringPrintf( "/usr/bin/dbus-send --type=signal --system / \"%s\" &", kUserCrashSignal); From 34f543b4c3370c532f95c067810bf9f7db6d2400 Mon Sep 17 00:00:00 2001 From: Yunlian Jiang Date: Wed, 27 Jun 2012 10:54:41 -0700 Subject: [PATCH 091/225] crash-reporter: change kDumpFormat to make it compile with gcc 4.7 BUG=None TEST=compile passed Change-Id: I9de18a4c91048881717731a88b265a09bfb22060 Reviewed-on: https://gerrit.chromium.org/gerrit/21687 Reviewed-by: Michael Krebs Tested-by: Yunlian Jiang Commit-Ready: Yunlian Jiang --- crash_reporter/kernel_collector.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index fc8500e8b..83eca8a16 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -12,7 +12,7 @@ static const char kDefaultKernelStackSignature[] = "kernel-UnspecifiedStackSignature"; static const char kDumpPath[] = "/dev/pstore"; -static const char kDumpFormat[] = "dmesg-ramoops-%d"; +static const char kDumpFormat[] = "dmesg-ramoops-%zu"; static const char kKernelExecName[] = "kernel"; // Maximum number of records to examine in the kDumpPath. static const size_t kMaxDumpRecords = 100; From acc7938ede0e3c07a2d1809f82b174b51f7ab7c5 Mon Sep 17 00:00:00 2001 From: Simon Que Date: Fri, 4 May 2012 18:10:09 -0700 Subject: [PATCH 092/225] Create separate udev collector class Previously added a udev collector to CrashCollector. Now it's time to put it in its own class. BUG=chromium-os:30268 TEST=See "TEST=" under https://gerrit.chromium.org/gerrit/21498 Change-Id: I6a62826cb84ef7ccd42a512b00e127af6de3280d Signed-off-by: Simon Que Reviewed-on: https://gerrit.chromium.org/gerrit/25112 Reviewed-by: Michael Krebs --- crash_reporter/Makefile | 2 + crash_reporter/crash_collector.cc | 72 +----------------- crash_reporter/crash_collector.h | 4 +- crash_reporter/crash_reporter.cc | 13 +++- crash_reporter/udev_collector.cc | 87 +++++++++++++++++++++ crash_reporter/udev_collector.h | 37 +++++++++ crash_reporter/udev_collector_test.cc | 105 ++++++++++++++++++++++++++ crash_reporter/user_collector.cc | 5 +- 8 files changed, 246 insertions(+), 79 deletions(-) create mode 100644 crash_reporter/udev_collector.cc create mode 100644 crash_reporter/udev_collector.h create mode 100644 crash_reporter/udev_collector_test.cc diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index d123a806e..51f18e89d 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -12,6 +12,7 @@ PKG_CONFIG ?= pkg-config CRASH_OBJS = \ crash_collector.o \ kernel_collector.o \ + udev_collector.o \ unclean_shutdown_collector.o \ user_collector.o @@ -48,6 +49,7 @@ all: CXX_BINARY(list_proxies) # Uncomment these to just run specific test(s). #TEST_BINS += crash_collector_test #TEST_BINS += kernel_collector_test +#TEST_BINS += udev_collector_test #TEST_BINS += unclean_shutdown_collector_test #TEST_BINS += user_collector_test diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index aaee3cb59..35d2e68f5 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -26,7 +26,6 @@ static const char kCollectChromeFile[] = "/mnt/stateful_partition/etc/collect_chrome_crashes"; -static const char kCollectUdevSignature[] = "crash_reporter-udev-collection"; static const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress"; static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf"; static const char kDefaultUserName[] = "chronos"; @@ -34,10 +33,7 @@ static const char kLeaveCoreFile[] = "/root/.leave_core"; static const char kLsbRelease[] = "/etc/lsb-release"; static const char kShellPath[] = "/bin/sh"; static const char kSystemCrashPath[] = "/var/spool/crash"; -static const char kUdevExecName[] = "udev"; -static const char kUdevSignatureKey[] = "sig"; static const char kUserCrashPath[] = "/home/chronos/user/crash"; -static const char kGzipPath[] = "/bin/gzip"; // Directory mode of the user crash spool directory. static const mode_t kUserCrashPathMode = 0755; @@ -60,7 +56,8 @@ const int CrashCollector::kMaxCrashDirectorySize = 32; CrashCollector::CrashCollector() : forced_crash_directory_(NULL), - lsb_release_(kLsbRelease) { + lsb_release_(kLsbRelease), + log_config_path_(kDefaultLogConfig) { } CrashCollector::~CrashCollector() { @@ -76,71 +73,6 @@ void CrashCollector::Initialize( is_feedback_allowed_function_ = is_feedback_allowed_function; } -bool CrashCollector::HandleUdevCrash(const std::string &udev_event) { - // Process the udev event string. - // The udev string should be formatted as follows: - // "ACTION=[action]:KERNEL=[name]:SUBSYSTEM=[subsystem]" - // The values don't have to be in any particular order. - - // First get all the key-value pairs. - std::vector > udev_event_keyval; - base::SplitStringIntoKeyValuePairs(udev_event, '=', ':', &udev_event_keyval); - std::vector >::const_iterator iter; - std::map udev_event_map; - for (iter = udev_event_keyval.begin(); - iter != udev_event_keyval.end(); - ++iter) { - udev_event_map[iter->first] = iter->second; - } - - // Construct the basename string for crash_reporter_logs.conf: - // "crash_reporter-udev-collection-[action]-[name]-[subsystem]" - // If a udev field is not provided, "" is used in its place, e.g.: - // "crash_reporter-udev-collection-[action]--[subsystem]" - // Hence, "" is used as a wildcard name string. - std::string basename = udev_event_map["ACTION"] + "-" + - udev_event_map["KERNEL"] + "-" + - udev_event_map["SUBSYSTEM"]; - std::string udev_log_name = std::string(kCollectUdevSignature) + '-' + - basename; - - // Make sure the crash directory exists, or create it if it doesn't. - FilePath crash_directory; - if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, NULL)) { - LOG(ERROR) << "Could not get crash directory."; - return false; - } - // Create the destination path. - std::string log_file_name = - FormatDumpBasename(basename, time(NULL), 0); - FilePath crash_path = GetCrashPath(crash_directory, log_file_name, "log"); - - // Handle the crash. - bool result = GetLogContents(FilePath(kDefaultLogConfig), udev_log_name, - crash_path); - if (!result) { - LOG(ERROR) << "Error reading udev log info " << udev_log_name; - return false; - } - - // Compress the output using gzip. - chromeos::ProcessImpl gzip_process; - gzip_process.AddArg(kGzipPath); - gzip_process.AddArg(crash_path.value()); - int process_result = gzip_process.Run(); - FilePath crash_path_zipped = FilePath(crash_path.value() + ".gz"); - // If the zip file was not created, use the uncompressed file. - if (process_result != 0 || !file_util::PathExists(crash_path_zipped)) - LOG(ERROR) << "Could not create zip file " << crash_path_zipped.value(); - else - crash_path = crash_path_zipped; - - AddCrashMetaData(kUdevSignatureKey, kCollectUdevSignature); - WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"), - kUdevExecName, crash_path.value()); - return true; -} - int CrashCollector::WriteNewFile(const FilePath &filename, const char *data, int size) { diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 77e21214c..650f5366a 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -28,9 +28,6 @@ class CrashCollector { void Initialize(CountCrashFunction count_crash, IsFeedbackAllowedFunction is_metrics_allowed); - // TODO(crosbug.com/30268): refactor into separate class. - bool HandleUdevCrash(const std::string &udev_event); - protected: friend class CrashCollectorTest; FRIEND_TEST(CrashCollectorTest, CheckHasCapacityCorrectBasename); @@ -149,6 +146,7 @@ class CrashCollector { std::string extra_metadata_; const char *forced_crash_directory_; const char *lsb_release_; + FilePath log_config_path_; }; #endif // _CRASH_REPORTER_CRASH_COLLECTOR_H_ diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index e563b45ea..de67326b4 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -15,6 +15,7 @@ #include "base/stringprintf.h" #include "chromeos/syslog_logging.h" #include "crash-reporter/kernel_collector.h" +#include "crash-reporter/udev_collector.h" #include "crash-reporter/unclean_shutdown_collector.h" #include "crash-reporter/user_collector.h" #include "gflags/gflags.h" @@ -46,6 +47,7 @@ enum CrashKinds { kCrashKindUncleanShutdown = 1, kCrashKindUser = 2, kCrashKindKernel = 3, + kCrashKindUdev = 4, kCrashKindMax }; @@ -70,6 +72,10 @@ static void CountKernelCrash() { SendCrashMetrics(kCrashKindKernel, "kernel"); } +static void CountUdevCrash() { + SendCrashMetrics(kCrashKindUdev, "udevcrash"); +} + static void CountUncleanShutdown() { SendCrashMetrics(kCrashKindUncleanShutdown, "uncleanshutdown"); } @@ -156,13 +162,13 @@ static int HandleUserCrash(UserCollector *user_collector) { return 0; } -static int HandleUdevCrash(CrashCollector *udev_collector) { +static int HandleUdevCrash(UdevCollector *udev_collector) { // Handle a crash indicated by a udev event. CHECK(!FLAGS_udev.empty()) << "--udev= must be set"; // Accumulate logs to help in diagnosing failures during user collection. chromeos::LogToString(true); - bool handled = udev_collector->HandleUdevCrash(FLAGS_udev); + bool handled = udev_collector->HandleCrash(FLAGS_udev); chromeos::LogToString(false); if (!handled) return 1; @@ -228,6 +234,8 @@ int main(int argc, char *argv[]) { UncleanShutdownCollector unclean_shutdown_collector; unclean_shutdown_collector.Initialize(CountUncleanShutdown, IsFeedbackAllowed); + UdevCollector udev_collector; + udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed); if (FLAGS_init) { return Initialize(&kernel_collector, @@ -246,7 +254,6 @@ int main(int argc, char *argv[]) { } if (!FLAGS_udev.empty()) { - CrashCollector udev_collector; return HandleUdevCrash(&udev_collector); } diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc new file mode 100644 index 000000000..0f7df3d0d --- /dev/null +++ b/crash_reporter/udev_collector.cc @@ -0,0 +1,87 @@ +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crash-reporter/udev_collector.h" + +#include "base/basictypes.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_split.h" +#include "base/string_util.h" +#include "chromeos/process.h" + +static const char kCollectUdevSignature[] = "crash_reporter-udev-collection"; +static const char kGzipPath[] = "/bin/gzip"; +static const char kUdevExecName[] = "udev"; +static const char kUdevSignatureKey[] = "sig"; + +UdevCollector::UdevCollector() {} + +UdevCollector::~UdevCollector() {} + +bool UdevCollector::HandleCrash(const std::string &udev_event) { + if (!is_feedback_allowed_function_()) { + LOG(ERROR) << "No consent given to collect crash info."; + return false; + } + + // Process the udev event string. + // First get all the key-value pairs. + std::vector > udev_event_keyval; + base::SplitStringIntoKeyValuePairs(udev_event, '=', ':', &udev_event_keyval); + std::vector >::const_iterator iter; + std::map udev_event_map; + for (iter = udev_event_keyval.begin(); + iter != udev_event_keyval.end(); + ++iter) { + udev_event_map[iter->first] = iter->second; + } + + // Construct the basename string for crash_reporter_logs.conf: + // "crash_reporter-udev-collection-[action]-[name]-[subsystem]" + // If a udev field is not provided, "" is used in its place, e.g.: + // "crash_reporter-udev-collection-[action]--[subsystem]" + // Hence, "" is used as a wildcard name string. + // TODO(sque, crosbug.com/32238): Implement wildcard checking. + std::string basename = udev_event_map["ACTION"] + "-" + + udev_event_map["KERNEL"] + "-" + + udev_event_map["SUBSYSTEM"]; + std::string udev_log_name = std::string(kCollectUdevSignature) + '-' + + basename; + + // Make sure the crash directory exists, or create it if it doesn't. + FilePath crash_directory; + if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, NULL)) { + LOG(ERROR) << "Could not get crash directory."; + return false; + } + // Create the destination path. + std::string log_file_name = + FormatDumpBasename(basename, time(NULL), 0); + FilePath crash_path = GetCrashPath(crash_directory, log_file_name, "log"); + + // Handle the crash. + bool result = GetLogContents(log_config_path_, udev_log_name, crash_path); + if (!result) { + LOG(ERROR) << "Error reading udev log info " << udev_log_name; + return false; + } + + // Compress the output using gzip. + chromeos::ProcessImpl gzip_process; + gzip_process.AddArg(kGzipPath); + gzip_process.AddArg(crash_path.value()); + int process_result = gzip_process.Run(); + FilePath crash_path_zipped = FilePath(crash_path.value() + ".gz"); + // If the zip file was not created, use the uncompressed file. + if (process_result != 0 || !file_util::PathExists(crash_path_zipped)) + LOG(ERROR) << "Could not create zip file " << crash_path_zipped.value(); + else + crash_path = crash_path_zipped; + + AddCrashMetaData(kUdevSignatureKey, kCollectUdevSignature); + WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"), + kUdevExecName, crash_path.value()); + return true; +} diff --git a/crash_reporter/udev_collector.h b/crash_reporter/udev_collector.h new file mode 100644 index 000000000..acb09cba0 --- /dev/null +++ b/crash_reporter/udev_collector.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CRASH_REPORTER_UDEV_COLLECTOR_H_ +#define _CRASH_REPORTER_UDEV_COLLECTOR_H_ + +#include + +#include "crash-reporter/crash_collector.h" +#include "gtest/gtest_prod.h" // for FRIEND_TEST + +class FilePath; + +// Udev crash collector. +class UdevCollector : public CrashCollector { + public: + UdevCollector(); + + virtual ~UdevCollector(); + + // The udev event string should be formatted as follows: + // "ACTION=[action]:KERNEL=[name]:SUBSYSTEM=[subsystem]" + // The values don't have to be in any particular order. One or more of them + // could be omitted, in which case it would be treated as a wildcard (*). + bool HandleCrash(const std::string &udev_event); + + private: + friend class UdevCollectorTest; + + // Mutator for unit testing. + void set_log_config_path(const std::string& path) { + log_config_path_ = FilePath(path); + } +}; + +#endif // _CRASH_REPORTER_UDEV_COLLECTOR_H_ diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc new file mode 100644 index 000000000..b75abfde5 --- /dev/null +++ b/crash_reporter/udev_collector_test.cc @@ -0,0 +1,105 @@ +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/scoped_temp_dir.h" +#include "chromeos/test_helpers.h" +#include "crash-reporter/udev_collector.h" +#include "gtest/gtest.h" + +namespace { + +// Dummy log config file name. +const char kLogConfigFileName[] = "log_config_file"; + +// A bunch of random rules to put into the dummy log config file. +const char kLogConfigFileContents[] = + "crash_reporter-udev-collection-change-card0-drm:echo change card0 drm\n" + "crash_reporter-udev-collection-add-state0-cpu:echo change state0 cpu\n" + "cros_installer:echo not for udev"; + +void CountCrash() {} + +bool s_consent_given = true; + +bool IsMetrics() { + return s_consent_given; +} + +// Returns the number of compressed crash log files found in the given path. +int GetNumLogFiles(const FilePath& path) { + file_util::FileEnumerator enumerator(path, false, + file_util::FileEnumerator::FILES, + "*.log.gz"); + int num_files = 0; + for (FilePath file_path = enumerator.Next(); + !file_path.value().empty(); + file_path = enumerator.Next()) { + num_files++; + } + return num_files; +} + +} // namespace + +class UdevCollectorTest : public ::testing::Test { + void SetUp() { + s_consent_given = true; + + collector_.reset(new UdevCollector); + collector_->Initialize(CountCrash, IsMetrics); + + temp_dir_generator_.reset(new ScopedTempDir()); + ASSERT_TRUE(temp_dir_generator_->CreateUniqueTempDir()); + EXPECT_TRUE(temp_dir_generator_->IsValid()); + + FilePath log_config_path = + temp_dir_generator_->path().Append(kLogConfigFileName); + collector_->log_config_path_ = log_config_path; + collector_->ForceCrashDirectory( + temp_dir_generator_->path().value().c_str()); + + // Write to a dummy log config file. + ASSERT_EQ(strlen(kLogConfigFileContents), + file_util::WriteFile(log_config_path, + kLogConfigFileContents, + strlen(kLogConfigFileContents))); + + chromeos::ClearLog(); + } + + protected: + scoped_ptr collector_; + scoped_ptr temp_dir_generator_; +}; + +TEST_F(UdevCollectorTest, TestNoConsent) { + s_consent_given = false; + collector_->HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm"); + EXPECT_EQ(0, GetNumLogFiles(temp_dir_generator_->path())); +} + +TEST_F(UdevCollectorTest, TestNoMatch) { + // No rule should match this. + collector_->HandleCrash("ACTION=change:KERNEL=foo:SUBSYSTEM=bar"); + EXPECT_EQ(0, GetNumLogFiles(temp_dir_generator_->path())); +} + +TEST_F(UdevCollectorTest, TestMatches) { + // Try multiple udev events in sequence. The number of log files generated + // should increase. + collector_->HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm"); + EXPECT_EQ(1, GetNumLogFiles(temp_dir_generator_->path())); + collector_->HandleCrash("ACTION=add:KERNEL=state0:SUBSYSTEM=cpu"); + EXPECT_EQ(2, GetNumLogFiles(temp_dir_generator_->path())); +} + +// TODO(sque, crosbug.com/32238) - test wildcard cases, multiple identical udev +// events. + +int main(int argc, char **argv) { + SetUpTests(&argc, argv, false); + return RUN_ALL_TESTS(); +} diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index b060bd69d..fca205848 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -43,7 +43,6 @@ static const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit"; static const char kCorePipeLimit[] = "4"; static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; -static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf"; static const char kStatePrefix[] = "State:\t"; const char *UserCollector::kUserId = "Uid:\t"; @@ -244,7 +243,7 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid, std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); std::string error_log = chromeos::GetLog(); FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog"); - if (GetLogContents(FilePath(kDefaultLogConfig), kCollectionErrorSignature, + if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature, diag_log_path)) { // We load the contents of diag_log into memory and append it to // the error log. We cannot just append to files because we need @@ -491,7 +490,7 @@ UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash( FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp"); FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); - if (GetLogContents(FilePath(kDefaultLogConfig), exec, log_path)) + if (GetLogContents(FilePath(log_config_path_), exec, log_path)) AddCrashMetaData("log", log_path.value()); ErrorType error_type = From 7c227576153a3773e35cd7ecfbd481099aca12ce Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 7 Aug 2012 23:25:10 -0400 Subject: [PATCH 093/225] fix -I flag paths when building out-of-tree The current build logic assumes the build dir is directly under the source dir. This fails if the build and source trees are completely different paths. So use a full path to the source dir rather than a relative one so that it works in all states. BUG=chromium-os:33327 TEST=`cros_run_unit_tests --board x86-alex -p crash-reporter` still works Change-Id: I0e6b4c257cbef5879cb67e8692e3082bbb5fb2d5 Reviewed-on: https://gerrit.chromium.org/gerrit/29577 Reviewed-by: Ben Chan Commit-Ready: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index 51f18e89d..648308f6f 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -20,7 +20,7 @@ PC_DEPS = glib-2.0 libpcrecpp libchrome-$(BASE_VER) libchromeos-$(BASE_VER) PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) PC_LIBS := $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) -CPPFLAGS += -I.. -I$(SYSROOT)/usr/include/google-breakpad $(PC_CFLAGS) +CPPFLAGS += -I$(SRC)/.. -I$(SYSROOT)/usr/include/google-breakpad $(PC_CFLAGS) LDLIBS = -lgflags $(PC_LIBS) From 2f3ed034f378e99f27bd733b6a452df47739117c Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Tue, 21 Aug 2012 20:17:03 -0700 Subject: [PATCH 094/225] crash-reporter: Also ignore renamed Chrome threads At some point Chrome started naming its threads such that crashes report a different executable name. We've been getting a *lot* of crash-reporter errors with names like "supplied_Compositor" in particular, so consider those to be Chrome crashes that should be ignored. Unfortunately, the thread names can be arbitrary. With this CL we check the entire range of possible names. Maybe someday someone can use the core file to determine that a process was originally named "chrome". BUG=chrome-os-partner:12045 TEST=Ran unittests Change-Id: Ia82d619bd1ee4367129640dc6c7be5ce258a68bb Reviewed-on: https://gerrit.chromium.org/gerrit/31084 Commit-Ready: Michael Krebs Tested-by: Michael Krebs Reviewed-by: Ben Chan --- crash_reporter/user_collector.cc | 81 ++++++++++++++++++++++++++- crash_reporter/user_collector_test.cc | 42 ++++++++++++++ 2 files changed, 121 insertions(+), 2 deletions(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index fca205848..c96d272e3 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -17,6 +17,7 @@ #include "base/file_util.h" #include "base/logging.h" +#include "base/stl_util.h" #include "base/string_split.h" #include "base/string_util.h" #include "base/stringprintf.h" @@ -526,6 +527,83 @@ bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes, return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name); } +/* Returns true if the given executable name matches that of Chrome. This + * includes checks for threads that Chrome has renamed. */ +static bool IsChromeExecName(const std::string &exec) { + static const char *kChromeNames[] = { + "chrome", + /* These come from the use of base::PlatformThread::SetName() directly */ + "CrBrowserMain", "CrRendererMain", "CrUtilityMain", "CrPPAPIMain", + "CrPPAPIBrokerMain", "CrPluginMain", "CrWorkerMain", "CrGpuMain", + "BrokerEvent", "CrVideoRenderer", "CrShutdownDetector", + "UsbEventHandler", "CrNaClMain", "CrServiceMain", + /* These thread names come from the use of base::Thread */ + "Gamepad polling thread", "Chrome_InProcGpuThread", + "Chrome_DragDropThread", "Renderer::FILE", "VC manager", + "VideoCaptureModuleImpl", "JavaBridge", "VideoCaptureManagerThread", + "Geolocation", "Geolocation_wifi_provider", + "Device orientation polling thread", "Chrome_InProcRendererThread", + "NetworkChangeNotifier", "Watchdog", "inotify_reader", + "cf_iexplore_background_thread", "BrowserWatchdog", + "Chrome_HistoryThread", "Chrome_SyncThread", "Chrome_ShellDialogThread", + "Printing_Worker", "Chrome_SafeBrowsingThread", "SimpleDBThread", + "D-Bus thread", "AudioThread", "NullAudioThread", "V4L2Thread", + "ChromotingClientDecodeThread", "Profiling_Flush", + "worker_thread_ticker", "AudioMixerAlsa", "AudioMixerCras", + "FakeAudioRecordingThread", "CaptureThread", + "Chrome_WebSocketproxyThread", "ProcessWatcherThread", + "Chrome_CameraThread", "import_thread", "NaCl_IOThread", + "Chrome_CloudPrintJobPrintThread", "Chrome_CloudPrintProxyCoreThread", + "DaemonControllerFileIO", "ChromotingMainThread", + "ChromotingEncodeThread", "ChromotingDesktopThread", + "ChromotingIOThread", "ChromotingFileIOThread", + "Chrome_libJingle_WorkerThread", "Chrome_ChildIOThread", + "GLHelperThread", "RemotingHostPlugin", + // "PAC thread #%d", // not easy to check because of "%d" + "Chrome_DBThread", "Chrome_WebKitThread", "Chrome_FileThread", + "Chrome_FileUserBlockingThread", "Chrome_ProcessLauncherThread", + "Chrome_CacheThread", "Chrome_IOThread", "Cache Thread", "File Thread", + "ServiceProcess_IO", "ServiceProcess_File", + "extension_crash_uploader", "gpu-process_crash_uploader", + "plugin_crash_uploader", "renderer_crash_uploader", + /* These come from the use of webkit_glue::WebThreadImpl */ + "Compositor", "Browser Compositor", + // "WorkerPool/%d", // not easy to check because of "%d" + /* These come from the use of base::Watchdog */ + "Startup watchdog thread Watchdog", "Shutdown watchdog thread Watchdog", + /* These come from the use of AudioDeviceThread::Start */ + "AudioDevice", "AudioInputDevice", + /* These come from the use of MessageLoopFactory::GetMessageLoop */ + "GpuVideoDecoder", "RtcVideoDecoderThread", "PipelineThread", + "AudioDecoderThread", "VideoDecoderThread", + /* These come from the use of MessageLoopFactory::GetMessageLoopProxy */ + "CaptureVideoDecoderThread", "CaptureVideoDecoder", + /* These come from the use of base::SimpleThread */ + "LocalInputMonitor/%d", // "%d" gets lopped off for kernel-supplied + /* These come from the use of base::DelegateSimpleThread */ + "ipc_channel_nacl reader thread/%d", "plugin_audio_input_thread/%d", + "plugin_audio_thread/%d", + /* These come from the use of base::SequencedWorkerPool */ + "BrowserBlockingWorker%d/%d", // "%d" gets lopped off for kernel-supplied + }; + static std::set chrome_names; + + /* Initialize a set of chrome names, for efficient lookup */ + if (chrome_names.empty()) { + for (size_t i = 0; i < arraysize(kChromeNames); i++) { + std::string check_name(kChromeNames[i]); + chrome_names.insert(check_name); + // When checking a kernel-supplied name, it should be truncated to 15 + // chars. See PR_SET_NAME in + // http://www.kernel.org/doc/man-pages/online/pages/man2/prctl.2.html, + // although that page misleads by saying "16 bytes". + chrome_names.insert("supplied_" + std::string(check_name, 0, 15)); + } + } + + return ContainsKey(chrome_names, exec); +} + bool UserCollector::ShouldDump(bool has_owner_consent, bool is_developer, bool handle_chrome_crashes, @@ -536,8 +614,7 @@ bool UserCollector::ShouldDump(bool has_owner_consent, // Treat Chrome crashes as if the user opted-out. We stop counting Chrome // crashes towards user crashes, so user crashes really mean non-Chrome // user-space crashes. - if ((exec == "chrome" || exec == "supplied_chrome") && - !handle_chrome_crashes) { + if (!handle_chrome_crashes && IsChromeExecName(exec)) { *reason = "ignoring - chrome crash"; return false; } diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 8fcbaefd6..290451292 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -151,6 +151,24 @@ TEST_F(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage) { EXPECT_FALSE(collector_.ShouldDump(false, false, false, "chrome", &reason)); EXPECT_EQ("ignoring - chrome crash", reason); + EXPECT_FALSE(collector_.ShouldDump(false, false, false, + "supplied_Compositor", &reason)); + EXPECT_EQ("ignoring - chrome crash", reason); + EXPECT_FALSE(collector_.ShouldDump(false, false, false, + "supplied_PipelineThread", &reason)); + EXPECT_EQ("ignoring - chrome crash", reason); + EXPECT_FALSE(collector_.ShouldDump(false, false, false, + "Chrome_ChildIOThread", &reason)); + EXPECT_EQ("ignoring - chrome crash", reason); + EXPECT_FALSE(collector_.ShouldDump(false, false, false, + "supplied_Chrome_ChildIOT", &reason)); + EXPECT_EQ("ignoring - chrome crash", reason); + EXPECT_FALSE(collector_.ShouldDump(false, false, false, + "supplied_ChromotingClien", &reason)); + EXPECT_EQ("ignoring - chrome crash", reason); + EXPECT_FALSE(collector_.ShouldDump(false, false, false, + "supplied_LocalInputMonit", &reason)); + EXPECT_EQ("ignoring - chrome crash", reason); // When running a developer image, test that chrome crashes are handled // when the "handle_chrome_crashes" flag is set. @@ -158,6 +176,30 @@ TEST_F(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage) { "chrome", &reason)); EXPECT_EQ("developer build - not testing - always dumping", reason); + EXPECT_TRUE(collector_.ShouldDump(false, true, true, + "supplied_Compositor", &reason)); + EXPECT_EQ("developer build - not testing - always dumping", + reason); + EXPECT_TRUE(collector_.ShouldDump(false, true, true, + "supplied_PipelineThread", &reason)); + EXPECT_EQ("developer build - not testing - always dumping", + reason); + EXPECT_TRUE(collector_.ShouldDump(false, true, true, + "Chrome_ChildIOThread", &reason)); + EXPECT_EQ("developer build - not testing - always dumping", + reason); + EXPECT_TRUE(collector_.ShouldDump(false, true, true, + "supplied_Chrome_ChildIOT", &reason)); + EXPECT_EQ("developer build - not testing - always dumping", + reason); + EXPECT_TRUE(collector_.ShouldDump(false, true, true, + "supplied_ChromotingClien", &reason)); + EXPECT_EQ("developer build - not testing - always dumping", + reason); + EXPECT_TRUE(collector_.ShouldDump(false, true, true, + "supplied_LocalInputMonit", &reason)); + EXPECT_EQ("developer build - not testing - always dumping", + reason); } TEST_F(UserCollectorTest, ShouldDumpUseConsentProductionImage) { From 65682f9007d58ecb9f2c0138a1774b4cda2db951 Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Thu, 6 Sep 2012 19:58:18 -0700 Subject: [PATCH 095/225] crash-reporter: Add "image_type" key to crash reports for dev builds For crash reports sent from developer images, annotate them with an "image_type" key set to "dev". This is so other people know they can be ignored. The key will be set to "force-official" if the $FORCE_OFFICIAL environment variable is set for non-developer images. BUG=chromium-os:33947 TEST=Ran logging_CrashSender autotest Change-Id: I9d87bdc7dac1a9fd4898fb3c7fd1c965c81d4ce3 Reviewed-on: https://gerrit.chromium.org/gerrit/32480 Tested-by: Michael Krebs Reviewed-by: Ben Chan Commit-Ready: Michael Krebs --- crash_reporter/crash_sender | 81 ++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 4b8473bfb..07302fb5a 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -18,6 +18,9 @@ CLEAN_UP_RUN_FILE=0 # contents includes our machine's anonymized guid. CONSENT_ID="/home/chronos/Consent To Send Stats" +# Path to file that indicates a crash test is currently running. +CRASH_TEST_IN_PROGRESS_FILE="/tmp/crash-test-in-progress" + # Path to find which is required for computing the crash rate. FIND="/usr/bin/find" @@ -28,8 +31,11 @@ FORCE_OFFICIAL=${FORCE_OFFICIAL:-0} # Path to hardware class description. HWCLASS_PATH="/sys/devices/platform/chromeos_acpi/HWID" -# Ignore PAUSE_CRASH_SENDING file if set. -OVERRIDE_PAUSE_SENDING=${OVERRIDE_PAUSE_SENDING:-0} +# Path to file that indicates this is a developer image. +LEAVE_CORE_FILE="/root/.leave_core" + +# Path to list_proxies. +LIST_PROXIES="/usr/bin/list_proxies" # Maximum crashes to send per day. MAX_CRASH_RATE=${MAX_CRASH_RATE:-32} @@ -41,6 +47,9 @@ METRICS_CLIENT="/usr/bin/metrics_client" # crash sending was successful, otherwise unsuccessful. MOCK_CRASH_SENDING="/tmp/mock-crash-sending" +# Ignore PAUSE_CRASH_SENDING file if set. +OVERRIDE_PAUSE_SENDING=${OVERRIDE_PAUSE_SENDING:-0} + # File whose existence causes crash sending to be delayed (for testing). # Must be stateful to enable testing kernel crashes. PAUSE_CRASH_SENDING="/var/lib/crash_sender_paused" @@ -48,6 +57,10 @@ PAUSE_CRASH_SENDING="/var/lib/crash_sender_paused" # URL to send official build crash reports to. REPORT_UPLOAD_PROD_URL="https://clients2.google.com/cr/report" +# Path to a directory of restricted certificates which includes +# a certificate for ${REPORT_UPLOAD_PROD_URL}. +RESTRICTED_CERTIFICATES_PATH="/usr/share/chromeos-ca-certificates" + # File whose existence implies we're running and not to start again. RUN_FILE="/var/run/crash_sender.pid" @@ -61,16 +74,9 @@ TAG="$(basename $0)[$$]" # hours. TIMESTAMPS_DIR="/var/lib/crash_sender" -# Path to a directory of restricted certificates which includes -# a certificate for ${REPORT_UPLOAD_PROD_URL}. -RESTRICTED_CERTIFICATES_PATH="/usr/share/chromeos-ca-certificates" - # Temp directory for this process. TMP_DIR="" -# Path to list_proxies. -LIST_PROXIES="/usr/bin/list_proxies" - lecho() { logger -t "${TAG}" "$@" } @@ -81,6 +87,12 @@ is_mock() { return 1 } +is_mock_successful() { + local mock_in=$(cat "${MOCK_CRASH_SENDING}") + [ "${mock_in}" = "" ] && return 0 # empty file means success + return 1 +} + cleanup() { if [ -n "${TMP_DIR}" ]; then rm -rf "${TMP_DIR}" @@ -117,11 +129,28 @@ check_not_already_running() { exit 1 } -is_official() { +is_official_image() { [ ${FORCE_OFFICIAL} -ne 0 ] && return 0 grep ^CHROMEOS_RELEASE_DESCRIPTION /etc/lsb-release | grep -q Official } +# Returns 0 if the a crash test is currently running. NOTE: Mirrors +# crash_collector.cc:CrashCollector::IsCrashTestInProgress(). +is_crash_test_in_progress() { + [ -f "${CRASH_TEST_IN_PROGRESS_FILE}" ] && return 0 + return 1 +} + +# Returns 0 if we should consider ourselves to be running on a developer +# image. NOTE: Mirrors crash_collector.cc:CrashCollector::IsDeveloperImage(). +is_developer_image() { + # If we're testing crash reporter itself, we don't want to special-case + # for developer images. + is_crash_test_in_progress && return 1 + [ -f "${LEAVE_CORE_FILE}" ] && return 0 + return 1 +} + # Generate a uniform random number in 0..max-1. generate_uniform_random() { local max=$1 @@ -218,6 +247,15 @@ send_crash() { local sig="$(get_key_value "${meta_path}" "sig")" local send_payload_size="$(stat --printf=%s "${report_payload}" 2>/dev/null)" + local image_type + if is_developer_image; then + image_type="dev" + elif [ ${FORCE_OFFICIAL} -ne 0 ]; then + image_type="force-official" + elif is_mock && ! is_mock_successful; then + image_type="mock-fail" + fi + local extra_key1="write_payload_size" local extra_value1="${write_payload_size}" local extra_key2="send_payload_size" @@ -234,16 +272,14 @@ send_crash() { fi local error_type="$(get_key_value "${meta_path}" "error_type")" - if [ "${error_type}" != "undefined" ]; then - extra_key3="error_type" - extra_value3="${error_type}" - fi + [ "${error_type}" = "undefined" ] && error_type= lecho "Sending crash:" lecho " Scheduled to send in ${sleep_time}s" lecho " Metadata: ${meta_path} (${kind})" lecho " Payload: ${report_payload}" lecho " Version: ${chromeos_version}" + [ -n "${image_type}" ] && lecho " Image type: ${image_type}" if is_mock; then lecho " Product: ${CHROMEOS_PRODUCT}" lecho " URL: ${url}" @@ -251,20 +287,16 @@ send_crash() { lecho " HWClass: ${hwclass}" lecho " ${extra_key1}: ${extra_value1}" lecho " ${extra_key2}: ${extra_value2}" - if [ -n "${extra_key3}" ]; then - lecho " ${extra_key3}: ${extra_value3}" - fi fi lecho " Exec name: ${exec_name}" + [ -n "${error_type}" ] && lecho " Error type: ${error_type}" if is_mock; then - local mock_in=$(cat "${MOCK_CRASH_SENDING}") - if [ "${mock_in}" = "" ]; then - lecho "Mocking successful send" - return 0 - else + if ! is_mock_successful; then lecho "Mocking unsuccessful send" return 1 fi + lecho "Mocking successful send" + return 0 fi if ! sleep ${sleep_time}; then @@ -291,9 +323,10 @@ send_crash() { -F "board=${board}" \ -F "hwclass=${hwclass}" \ -F "exec_name=${exec_name}" \ + ${image_type:+-F "image_type=${image_type}"} \ + ${error_type:+-F "error_type=${error_type}"} \ -F "${extra_key1}=${extra_value1}" \ -F "${extra_key2}=${extra_value2}" \ - ${extra_key3:+-F "${extra_key3}=${extra_value3}"} \ -F "guid=<${CONSENT_ID}" -o "${report_id}" 2>"${curl_stderr}" curl_result=$? set -e @@ -364,7 +397,7 @@ send_crashes() { continue fi - if ! is_mock && ! is_official; then + if ! is_mock && ! is_official_image; then lecho "Not an official OS version. Removing crash." remove_report "${meta_path}" continue From a1cc38388b371e3af5622edf57cc0e2b4991a3cd Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Thu, 13 Sep 2012 13:24:12 -0700 Subject: [PATCH 096/225] crash-reporter: Add a missing Chrome thread to ignore I somehow missed "AudioOutputDevice" as the name of a Chrome thread. It showed up in crash reports. BUG=chrome-os-partner:12045 TEST=Ran unittests Change-Id: Ia29404aebfe806a81fceb6b45145434af6749822 Reviewed-on: https://gerrit.chromium.org/gerrit/33224 Commit-Ready: Michael Krebs Tested-by: Michael Krebs Reviewed-by: Ben Chan --- crash_reporter/user_collector.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index c96d272e3..d3ee66d44 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -572,7 +572,7 @@ static bool IsChromeExecName(const std::string &exec) { /* These come from the use of base::Watchdog */ "Startup watchdog thread Watchdog", "Shutdown watchdog thread Watchdog", /* These come from the use of AudioDeviceThread::Start */ - "AudioDevice", "AudioInputDevice", + "AudioDevice", "AudioInputDevice", "AudioOutputDevice", /* These come from the use of MessageLoopFactory::GetMessageLoop */ "GpuVideoDecoder", "RtcVideoDecoderThread", "PipelineThread", "AudioDecoderThread", "VideoDecoderThread", From bc2d7dde163e0a2bbb7a122053620a4bddcd0411 Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Wed, 12 Sep 2012 20:31:20 -0700 Subject: [PATCH 097/225] crash-reporter: Don't send dmesg output with internal crash_reporter reports Seeing as kernel crashes no longer include dmesg output, we probably shouldn't either. This is for crash reports with signature "crash_reporter-user-collection". BUG=chromium-os:34389 TEST=Ran logging_UserCrash autotest CQ-DEPEND=I813a711c3557227cc17a58be8f960bfe569722bc Change-Id: I52debf6d8ad32132d3b64c7f313f306b26287dd1 Reviewed-on: https://gerrit.chromium.org/gerrit/33167 Tested-by: Michael Krebs Reviewed-by: Ben Chan Commit-Ready: Michael Krebs --- crash_reporter/crash_reporter_logs.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 5650346ba..c70b2c13d 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -25,7 +25,7 @@ cros_installer:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log # as it is stored in memory. The output format specified for 'ps' is the # same as with the "u" ("user-oriented") option, except it doesn't show # the commands' arguments (i.e. "comm" instead of "command"). -crash_reporter-user-collection:echo "===ps output==="; ps axw -o user,pid,%cpu,%mem,vsz,rss,tname,stat,start_time,bsdtime,comm | tail -c 25000; echo "===dmesg output==="; dmesg | tail -c 25000; echo "===meminfo==="; cat /proc/meminfo +crash_reporter-user-collection:echo "===ps output==="; ps axw -o user,pid,%cpu,%mem,vsz,rss,tname,stat,start_time,bsdtime,comm | tail -c 25000; echo "===meminfo==="; cat /proc/meminfo # This rule is similar to the crash_reporter-user-collection rule, except it is # run for kernel errors reported through udev events. From 8e95fa9faabb9a6b4de9290f1ecce31aae1aff63 Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Thu, 13 Sep 2012 16:23:11 -0700 Subject: [PATCH 098/225] crash-reporter: Add "boot_mode" key to crash reports for dev mode For crash reports sent when a machine is in developer mode, annotate them with a "boot_mode" key set to "dev". This is so other people know the system may have been mucked with. BUG=chromium-os:33947 TEST=Ran logging_CrashSender autotest Change-Id: I30d58885e41f2c32e32b7b0f691ed2e5d14f2e3c Reviewed-on: https://gerrit.chromium.org/gerrit/33246 Tested-by: Michael Krebs Reviewed-by: Ben Chan Commit-Ready: Michael Krebs --- crash_reporter/crash_sender | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 07302fb5a..ab1898f1f 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -47,6 +47,10 @@ METRICS_CLIENT="/usr/bin/metrics_client" # crash sending was successful, otherwise unsuccessful. MOCK_CRASH_SENDING="/tmp/mock-crash-sending" +# Set this to 1 in the environment to pretend to have booted in developer +# mode. This is used by autotests. +MOCK_DEVELOPER_MODE=${MOCK_DEVELOPER_MODE:-0} + # Ignore PAUSE_CRASH_SENDING file if set. OVERRIDE_PAUSE_SENDING=${OVERRIDE_PAUSE_SENDING:-0} @@ -151,6 +155,15 @@ is_developer_image() { return 1 } +# Returns 0 if the machine booted up in developer mode. +is_developer_mode() { + [ ${MOCK_DEVELOPER_MODE} -ne 0 ] && return 0 + # If we're testing crash reporter itself, we don't want to special-case + # for developer mode. + is_crash_test_in_progress && return 1 + crossystem "devsw_boot?1" # exit status will be accurate +} + # Generate a uniform random number in 0..max-1. generate_uniform_random() { local max=$1 @@ -256,6 +269,15 @@ send_crash() { image_type="mock-fail" fi + local boot_mode + if ! crossystem "cros_debug" > /dev/null 2>&1; then + # Sanity-check failed that makes sure crossystem exists. + lecho "Cannot determine boot mode due to error running crossystem command" + boot_mode="missing-crossystem" + elif is_developer_mode; then + boot_mode="dev" + fi + local extra_key1="write_payload_size" local extra_value1="${write_payload_size}" local extra_key2="send_payload_size" @@ -280,6 +302,7 @@ send_crash() { lecho " Payload: ${report_payload}" lecho " Version: ${chromeos_version}" [ -n "${image_type}" ] && lecho " Image type: ${image_type}" + [ -n "${boot_mode}" ] && lecho " Boot mode: ${boot_mode}" if is_mock; then lecho " Product: ${CHROMEOS_PRODUCT}" lecho " URL: ${url}" @@ -324,6 +347,7 @@ send_crash() { -F "hwclass=${hwclass}" \ -F "exec_name=${exec_name}" \ ${image_type:+-F "image_type=${image_type}"} \ + ${boot_mode:+-F "boot_mode=${boot_mode}"} \ ${error_type:+-F "error_type=${error_type}"} \ -F "${extra_key1}=${extra_value1}" \ -F "${extra_key2}=${extra_value2}" \ From d9067dcdf7c06e7db2df7134cb534f9869dc99a5 Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Thu, 1 Nov 2012 17:25:41 -0400 Subject: [PATCH 099/225] Get the real extension if log file is gzipped Udev collector compresses crash log and appends .gz as file extension which is not recognized by crash sender. This patch fixes crash sender by extracting real file extension if the original log file ends with .gz. BUG=chromium-os:35968 TEST=Manually test, crash sender can send udev collector generated log file: report id 7fa1184e8e62946f. Change-Id: Ic0abd9f264ef601399cc5edd27faa6f64cd9f204 Reviewed-on: https://gerrit.chromium.org/gerrit/37136 Reviewed-by: Michael Krebs Reviewed-by: Simon Que Commit-Ready: Yufeng Shen Tested-by: Yufeng Shen --- crash_reporter/crash_sender | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index ab1898f1f..97045d408 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -202,7 +202,14 @@ get_base() { } get_extension() { - echo "${1##*.}" + local extension="${1##*.}" + local filename="${1%.*}" + # For gzipped file, we ignore .gz and get the real extension + if [ "${extension}" = "gz" ]; then + echo "${filename##*.}" + else + echo "${extension}" + fi } # Return which kind of report the given metadata file relates to From 20cb7ccc6d5e2f6cf6c91e945479ddfeec00a6e5 Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Fri, 2 Nov 2012 14:21:21 -0400 Subject: [PATCH 100/225] Construct signature and exec_name from udev rules Current all udev collected reports have the same signature and exec_name. This patch makes that different udev rules generate reports with different signatures and exec_name so that it is easier to search a specific kind of udev report on crash server. BUG=chromium-os:35169 TEST=Tested locally, with a fake udev rule ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="cyapa", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-cyapa:ACTION=change" and fake crash_reporter_logs.conf entry crash_reporter-udev-collection-change--i2c-cyapa:grep -C 20 cyapa /var/log/messages Generate a report log with meta data: " sig=crash_reporter-udev-collection-change--i2c-cyapa exec_name=udev-i2c-cyapa ver=3120.0.2012_10_31_1248 payload=/var/spool/crash/change__i2c_cyapa.20121102.125731.0.log.gz payload_size=6557 done=1 " Change-Id: I9f7905b69e786a2a13cb8a8df46eeba243157009 Reviewed-on: https://gerrit.chromium.org/gerrit/37262 Reviewed-by: Simon Que Reviewed-by: Michael Krebs Commit-Ready: Yufeng Shen Tested-by: Yufeng Shen --- crash_reporter/udev_collector.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc index 0f7df3d0d..138ec4bda 100644 --- a/crash_reporter/udev_collector.cc +++ b/crash_reporter/udev_collector.cc @@ -80,8 +80,10 @@ bool UdevCollector::HandleCrash(const std::string &udev_event) { else crash_path = crash_path_zipped; - AddCrashMetaData(kUdevSignatureKey, kCollectUdevSignature); + std::string exec_name = std::string(kUdevExecName) + "-" + + udev_event_map["SUBSYSTEM"]; + AddCrashMetaData(kUdevSignatureKey, udev_log_name); WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"), - kUdevExecName, crash_path.value()); + exec_name, crash_path.value()); return true; } From bbda0787c9d84b577995e9d44039681ed9d66fd4 Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Mon, 5 Nov 2012 17:06:34 -0500 Subject: [PATCH 101/225] crash-reporter: Add udev rule to collect cyapa trackpad logs Add the udev rule that when trackpad driver cyapa detects abnormal behavior and fired uevent, additional logs from system logs are collected. BUG=chromium-os:35169 TEST=Made fake cyapa uevent and checked that logs are generated in /var/spool/crash Change-Id: I7ebabd729e6ce362b6f5db12c7cff38ed75c9792 Reviewed-on: https://gerrit.chromium.org/gerrit/37376 Reviewed-by: Michael Krebs Commit-Ready: Yufeng Shen Tested-by: Yufeng Shen --- crash_reporter/99-crash-reporter.rules | 2 ++ crash_reporter/crash_reporter_logs.conf | 3 +++ 2 files changed, 5 insertions(+) diff --git a/crash_reporter/99-crash-reporter.rules b/crash_reporter/99-crash-reporter.rules index 25a1504c2..a0658210f 100644 --- a/crash_reporter/99-crash-reporter.rules +++ b/crash_reporter/99-crash-reporter.rules @@ -1 +1,3 @@ ACTION=="change", SUBSYSTEM=="drm", KERNEL=="card0", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=KERNEL=card0:SUBSYSTEM=drm:ACTION=change" +# For detecting cypress trackpad issue. Passing into crash_reporter SUBSYSTEM=i2c-cyapa since crash_reporter does not handle DRIVER string. +ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="cyapa", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-cyapa:ACTION=change" diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index c70b2c13d..70ed25841 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -31,6 +31,9 @@ crash_reporter-user-collection:echo "===ps output==="; ps axw -o user,pid,%cpu,% # run for kernel errors reported through udev events. crash_reporter-udev-collection-change-card0-drm:for dri in /sys/kernel/debug/dri/*; do echo "===$dri/i915_error_state==="; cat $dri/i915_error_state; done +# When trackpad driver cyapa detects some abnormal behavior, we collect +# additional logs from kernel messages. +crash_reporter-udev-collection-change--i2c-cyapa:grep cyapa /var/log/messages # The following rules are only for testing purposes. crash_log_test:echo hello world crash_log_recursion_test:sleep 1 && /usr/local/autotest/tests/crash_log_recursion_test From b1b91a52dd821e0c94f2ad162f6a135c349f2e6b Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Mon, 26 Nov 2012 14:26:17 -0800 Subject: [PATCH 102/225] crash-reporter: Add a new Chrome thread to ignore A bunch of "supplied_MediaPipeline" crashes are showing up for product "Chrome_ChromeOS", so add "MediaPipeline" as a thread name to ignore as a Chrome crash. BUG=chrome-os-partner:16165 TEST=Ran unittests Change-Id: Iadcba53400c63b1e7fe93d7f1e74c8dd5c37ebd9 Reviewed-on: https://gerrit.chromium.org/gerrit/38673 Commit-Ready: Michael Krebs Tested-by: Michael Krebs Reviewed-by: Ben Chan --- crash_reporter/user_collector.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index d3ee66d44..ed3fcf53a 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -532,6 +532,8 @@ bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes, static bool IsChromeExecName(const std::string &exec) { static const char *kChromeNames[] = { "chrome", + /* These are additional thread names seen in http://crash/ */ + "MediaPipeline", /* These come from the use of base::PlatformThread::SetName() directly */ "CrBrowserMain", "CrRendererMain", "CrUtilityMain", "CrPPAPIMain", "CrPPAPIBrokerMain", "CrPluginMain", "CrWorkerMain", "CrGpuMain", From ed61be67b0845a76f7347171a5aa780d2ce88028 Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Wed, 5 Dec 2012 15:13:08 -0800 Subject: [PATCH 103/225] crash-reporter: Collect i915 error state for chrome crashes Add in the i915 error state as a log file in "chrome" crash reports. "chrome" isn't normally reported through crash_reporter, so this will only kick in where a developer has explicitly changed a machine to report them as such. Because the output can be quite large (as we saw with udev crash reports), the log is compressed before uploading. Although the file attached to the crash report will have a ".log" extension, it will actually have been gzip'ed. BUG=chromium-os:36979 TEST=Tested same config with "sleep" command: crash/reportdetail?reportid=f429dbcaee7494be Change-Id: I618a4364dcdd751852a69a79452272b7cf5e61f8 Reviewed-on: https://gerrit.chromium.org/gerrit/39271 Commit-Ready: Michael Krebs Tested-by: Michael Krebs Reviewed-by: Stuart Abercrombie --- crash_reporter/crash_reporter_logs.conf | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 70ed25841..6f8ad343b 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -31,6 +31,15 @@ crash_reporter-user-collection:echo "===ps output==="; ps axw -o user,pid,%cpu,% # run for kernel errors reported through udev events. crash_reporter-udev-collection-change-card0-drm:for dri in /sys/kernel/debug/dri/*; do echo "===$dri/i915_error_state==="; cat $dri/i915_error_state; done +# TODO(mkrebs,sabercrombie): Because these can be quite large (600KB +# compressed), remove this when GPU hangs are no longer an issue, and/or we +# start collecting "chrome" crashes for normal images. +# Collect i915 error state for Chrome crashes as well. See +# crosbug.com/36979. These can be big (i.e. megabytes), so compress them with +# gzip. Note that the file attached to the crash report will have a ".log" +# extension, but will have been gzip'ed. +chrome:for dri in /sys/kernel/debug/dri/*; do echo "===$dri/i915_error_state==="; cat $dri/i915_error_state; done | gzip -c + # When trackpad driver cyapa detects some abnormal behavior, we collect # additional logs from kernel messages. crash_reporter-udev-collection-change--i2c-cyapa:grep cyapa /var/log/messages From 0b783111866ed6bc660717b2537c448e3b3e6e57 Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Thu, 20 Dec 2012 12:19:55 -0500 Subject: [PATCH 104/225] crash-reporter: Add rule to collect touch noise logs We are having a daemon touch_noise_monitor which can monitor raw touch device events and when identifying noise pattern, it will call crash reporter to upload relevant touch logs. Adding this matching rule to make this happen. BUG=chrome-os-partner:16788 TEST=Made fake touch noise happen and check that crash reported as reportid c34d76e413aa87ee Change-Id: I2fa9a32d97a3f31f183623bd44cb3eb8f2436878 Reviewed-on: https://gerrit.chromium.org/gerrit/40014 Commit-Queue: Yufeng Shen Reviewed-by: Yufeng Shen Tested-by: Yufeng Shen --- crash_reporter/crash_reporter_logs.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 6f8ad343b..e166b88f3 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -43,6 +43,8 @@ chrome:for dri in /sys/kernel/debug/dri/*; do echo "===$dri/i915_error_state===" # When trackpad driver cyapa detects some abnormal behavior, we collect # additional logs from kernel messages. crash_reporter-udev-collection-change--i2c-cyapa:grep cyapa /var/log/messages +# When touch device noise are detected, we collect relevant logs. (crosbug.com/p/16788) +crash_reporter-udev-collection---TouchNoise:cat /var/log/touch_noise.log # The following rules are only for testing purposes. crash_log_test:echo hello world crash_log_recursion_test:sleep 1 && /usr/local/autotest/tests/crash_log_recursion_test From 836353a3b5bd52ea776cc3455c3cad4055c7decc Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Tue, 15 Jan 2013 17:01:36 -0500 Subject: [PATCH 105/225] crash_sender: Temporary hack to increase MAX_CRASH_RATE to 100 for Link BUG=chrome-os-partner:17244 TEST=On Link 1) Run crash_sender, see log 2013-01-15T16:10:12.774799-08:00 localhost crash_sender[6163]: MAX_CRASH_RATE set to 100 2) mv /etc/session_manager_use_flags.txt /etc/session_manager_use_flags.txt.bak Run crash_sender check log 2013-01-15T16:12:35.718920-08:00 localhost crash_sender[6730]: MAX_CRASH_RATE set to 32 Change-Id: Ib0311be802993206b1a7d55b62399d5c935a656d Reviewed-on: https://gerrit.chromium.org/gerrit/41305 Reviewed-by: Michael Krebs Tested-by: Yufeng Shen Commit-Queue: Yufeng Shen --- crash_reporter/crash_sender | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 97045d408..e29f9b9db 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -468,6 +468,39 @@ send_crashes() { done } +USE_FLAGS_PATH="/etc/session_manager_use_flags.txt" +# Returns success if the USE flag passed as its sole parameter was defined. +# New flags must be first be added to the ebuild file. +use_flag_is_set() { + local use_flags flag i + flag="$1" + if [ ! -e ${USE_FLAGS_PATH} ]; then + use_flags="" + else + use_flags="$(cat ${USE_FLAGS_PATH})" + fi + lecho "use_flags : $use_flags" + for i in $use_flags; do + if [ $i = "${flag}" ]; then + return 0 + fi + done + return 1 +} + +# Returns success if we were built for the board passed as the sole parameter. +# Not all boards are handled; see the ebuild file. +is_board() { + use_flag_is_set "board_use_$1" +} + +set_max_crash_rate_link() { + if is_board link; then + MAX_CRASH_RATE=100 + fi + lecho "MAX_CRASH_RATE set to $MAX_CRASH_RATE" +} + main() { trap cleanup EXIT INT TERM if [ -e "${PAUSE_CRASH_SENDING}" ] && \ @@ -488,6 +521,10 @@ main() { TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXX)" + # Temporary hack to set MAX_CRASH_RATE, see chrome-os-partner:17244 + # TODO(miletus)Once R23 is live for Link, we should remove this. + set_max_crash_rate_link + # Send system-wide crashes send_crashes "/var/spool/crash" From ebb86ae82e7ed843683fd6b3502f669945be1c37 Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Tue, 15 Jan 2013 19:55:09 -0500 Subject: [PATCH 106/225] crash-reporter: Add rule to collect touch event log We are having a daemon touch_noise_monitor which periodically dump touch event logs to /var/log/touch_event.log. Adding this matching rule to collect the logs. BUG=chrome-os-partner:17244 TEST=Made fake touch event /var/log/touch_event.log and call /sbin/crash_reporter --udev=SUBSYSTEM=TouchEvent to generate crash report. Change-Id: Ic2eaea021a3eef31f48c92688600171db961c09f Reviewed-on: https://gerrit.chromium.org/gerrit/41325 Reviewed-by: Michael Krebs Commit-Queue: Yufeng Shen Tested-by: Yufeng Shen --- crash_reporter/crash_reporter_logs.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index e166b88f3..0e233d9c8 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -45,6 +45,8 @@ chrome:for dri in /sys/kernel/debug/dri/*; do echo "===$dri/i915_error_state===" crash_reporter-udev-collection-change--i2c-cyapa:grep cyapa /var/log/messages # When touch device noise are detected, we collect relevant logs. (crosbug.com/p/16788) crash_reporter-udev-collection---TouchNoise:cat /var/log/touch_noise.log +# Periodically collect touch event log for debugging (crosbug.com/p/17244) +crash_reporter-udev-collection---TouchEvent:cat /var/log/touch_event.log # The following rules are only for testing purposes. crash_log_test:echo hello world crash_log_recursion_test:sleep 1 && /usr/local/autotest/tests/crash_log_recursion_test From 76c4ac13e9cb777c01275358d7fc3cc301902a04 Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Wed, 16 Jan 2013 10:31:43 -0800 Subject: [PATCH 107/225] Revert "crash_sender: Temporary hack to increase MAX_CRASH_RATE to 100 for Link" This reverts commit fc5783575be6a6c1c56895ebe99440058ff67fe9 Change-Id: I9034f9b46e20603274d6a7e199665eb0e3dae3fb Reviewed-on: https://gerrit.chromium.org/gerrit/41363 Commit-Queue: Yufeng Shen Reviewed-by: Yufeng Shen Tested-by: Yufeng Shen --- crash_reporter/crash_sender | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index e29f9b9db..97045d408 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -468,39 +468,6 @@ send_crashes() { done } -USE_FLAGS_PATH="/etc/session_manager_use_flags.txt" -# Returns success if the USE flag passed as its sole parameter was defined. -# New flags must be first be added to the ebuild file. -use_flag_is_set() { - local use_flags flag i - flag="$1" - if [ ! -e ${USE_FLAGS_PATH} ]; then - use_flags="" - else - use_flags="$(cat ${USE_FLAGS_PATH})" - fi - lecho "use_flags : $use_flags" - for i in $use_flags; do - if [ $i = "${flag}" ]; then - return 0 - fi - done - return 1 -} - -# Returns success if we were built for the board passed as the sole parameter. -# Not all boards are handled; see the ebuild file. -is_board() { - use_flag_is_set "board_use_$1" -} - -set_max_crash_rate_link() { - if is_board link; then - MAX_CRASH_RATE=100 - fi - lecho "MAX_CRASH_RATE set to $MAX_CRASH_RATE" -} - main() { trap cleanup EXIT INT TERM if [ -e "${PAUSE_CRASH_SENDING}" ] && \ @@ -521,10 +488,6 @@ main() { TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXX)" - # Temporary hack to set MAX_CRASH_RATE, see chrome-os-partner:17244 - # TODO(miletus)Once R23 is live for Link, we should remove this. - set_max_crash_rate_link - # Send system-wide crashes send_crashes "/var/spool/crash" From 916fcd902e08da58e19f4e9f5acf5018ce12bab4 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 17 Jan 2013 10:57:06 -0500 Subject: [PATCH 108/225] update OWNERS Mike is taking over for Mike. BUG=None TEST=None Change-Id: I031fd82f7015be19f8232ee77b516699b757cd94 Reviewed-on: https://gerrit.chromium.org/gerrit/41532 Reviewed-by: Ben Chan Tested-by: Mike Frysinger --- crash_reporter/OWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/OWNERS b/crash_reporter/OWNERS index 8c1ac9e10..c5b51d0e6 100644 --- a/crash_reporter/OWNERS +++ b/crash_reporter/OWNERS @@ -1,2 +1,2 @@ benchan@chromium.org -mkrebs@chromium.org +vapier@chromium.org From 1c57e9e330bc4c6a44955fd649ee9a4badb9e1dc Mon Sep 17 00:00:00 2001 From: Michael Krebs Date: Tue, 25 Sep 2012 18:03:13 -0700 Subject: [PATCH 109/225] crash-reporter: Remove some dependency on /proc Make the existence of a crashing process's /proc// directory optional for all of crash_reporter, except for core2md. In particular, have the kernel pass the UID of the crashing process to crash_reporter in case the "status" file is unavailable. BUG=chromium-os:34385 TEST=Ran unittests and autotests CQ-DEPEND=I6048d3eb84f8188bee6a755eaa010510f5d2459b Change-Id: I62df52cab44cf1febc7ed3e55b75bcffa0daf524 Reviewed-on: https://gerrit.chromium.org/gerrit/34078 Commit-Queue: David James Reviewed-by: David James Tested-by: David James --- crash_reporter/user_collector.cc | 102 ++++++++++++++++---------- crash_reporter/user_collector.h | 10 +-- crash_reporter/user_collector_test.cc | 24 ++++-- 3 files changed, 85 insertions(+), 51 deletions(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index ed3fcf53a..cc4beead2 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -46,6 +46,9 @@ static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; static const char kStatePrefix[] = "State:\t"; +// Define an otherwise invalid value that represents an unknown UID. +static const uid_t kUnknownUid = -1; + const char *UserCollector::kUserId = "Uid:\t"; const char *UserCollector::kGroupId = "Gid:\t"; @@ -90,13 +93,17 @@ std::string UserCollector::GetErrorTypeSignature(ErrorType error_type) const { } } +// Return the string that should be used for the kernel's core_pattern file. +// Note that if you change the format of the enabled pattern, you'll probably +// also need to change the ParseCrashAttributes() function below, the +// user_collector_test.cc unittest, and the logging_UserCrash.py autotest. std::string UserCollector::GetPattern(bool enabled) const { if (enabled) { - // Combine the three crash attributes into one parameter to try to reduce - // the size of the invocation line for crash_reporter since the kernel - // has a fixed-sized (128B) buffer that it will truncate into. Note that - // the kernel does not support quoted arguments in core_pattern. - return StringPrintf("|%s --user=%%p:%%s:%%e", our_path_.c_str()); + // Combine the four crash attributes into one parameter to try to reduce + // the size of the invocation line for crash_reporter, since the kernel + // has a fixed-sized (128B) buffer for it (before parameter expansion). + // Note that the kernel does not support quoted arguments in core_pattern. + return StringPrintf("|%s --user=%%p:%%s:%%u:%%e", our_path_.c_str()); } else { return "core"; } @@ -158,7 +165,7 @@ bool UserCollector::GetSymlinkTarget(const FilePath &symlink, return true; } -bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid, +bool UserCollector::GetExecutableBaseNameFromPid(pid_t pid, std::string *base_name) { FilePath target; FilePath process_path = GetProcessPath(pid); @@ -343,7 +350,7 @@ UserCollector::ErrorType UserCollector::ValidateCoreFile( return kErrorNone; } -bool UserCollector::GetCreatedCrashDirectory(pid_t pid, +bool UserCollector::GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid, FilePath *crash_file_path, bool *out_of_capacity) { FilePath process_path = GetProcessPath(pid); @@ -352,32 +359,39 @@ bool UserCollector::GetCreatedCrashDirectory(pid_t pid, LOG(ERROR) << "Purposefully failing to create spool directory"; return false; } - if (!file_util::ReadFileToString(process_path.Append("status"), - &status)) { - LOG(ERROR) << "Could not read status file"; + + uid_t uid; + if (file_util::ReadFileToString(process_path.Append("status"), &status)) { + std::vector status_lines; + base::SplitString(status, '\n', &status_lines); + + std::string process_state; + if (!GetStateFromStatus(status_lines, &process_state)) { + LOG(ERROR) << "Could not find process state in status file"; + return false; + } + LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state; + + // Get effective UID of crashing process. + int id; + if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &id)) { + LOG(ERROR) << "Could not find euid in status file"; + return false; + } + uid = id; + } else if (supplied_ruid != kUnknownUid) { + LOG(INFO) << "Using supplied UID " << supplied_ruid + << " for crashed process [" << pid + << "] due to error reading status file"; + uid = supplied_ruid; + } else { + LOG(ERROR) << "Could not read status file and kernel did not supply UID"; LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: " << file_util::DirectoryExists(process_path); return false; } - std::vector status_lines; - base::SplitString(status, '\n', &status_lines); - - std::string process_state; - if (!GetStateFromStatus(status_lines, &process_state)) { - LOG(ERROR) << "Could not find process state in status file"; - return false; - } - LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state; - - int process_euid; - if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &process_euid)) { - LOG(ERROR) << "Could not find euid in status file"; - return false; - } - if (!GetCreatedCrashDirectoryByEuid(process_euid, - crash_file_path, - out_of_capacity)) { + if (!GetCreatedCrashDirectoryByEuid(uid, crash_file_path, out_of_capacity)) { LOG(ERROR) << "Could not create crash directory"; return false; } @@ -471,16 +485,18 @@ UserCollector::ErrorType UserCollector::ConvertCoreToMinidump( } UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash( - int pid, const std::string &exec, bool *out_of_capacity) { + pid_t pid, const std::string &exec, uid_t supplied_ruid, + bool *out_of_capacity) { FilePath crash_path; - if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) { + if (!GetCreatedCrashDirectory(pid, supplied_ruid, &crash_path, + out_of_capacity)) { LOG(ERROR) << "Unable to find/create process-specific crash path"; return kErrorSystemIssue; } // Directory like /tmp/crash_reporter/1234 which contains the // procfs entries and other temporary files used during conversion. - FilePath container_dir(StringPrintf("/tmp/crash_reporter/%d", pid)); + FilePath container_dir(StringPrintf("/tmp/crash_reporter/%d", (int)pid)); // Delete a pre-existing directory from crash reporter that may have // been left around for diagnostics from a failed conversion attempt. // If we don't, existing files can cause forking to fail. @@ -521,10 +537,18 @@ UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash( } bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes, - pid_t *pid, int *signal, + pid_t *pid, int *signal, uid_t *uid, std::string *kernel_supplied_name) { - pcrecpp::RE re("(\\d+):(\\d+):(.*)"); - return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name); + pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(.*)"); + if (re.FullMatch(crash_attributes, pid, signal, uid, kernel_supplied_name)) + return true; + + LOG(INFO) << "Falling back to parsing crash attributes '" + << crash_attributes << "' without UID"; + pcrecpp::RE re_without_uid("(\\d+):(\\d+):(.*)"); + *uid = kUnknownUid; + return re_without_uid.FullMatch(crash_attributes, pid, signal, + kernel_supplied_name); } /* Returns true if the given executable name matches that of Chrome. This @@ -641,11 +665,12 @@ bool UserCollector::ShouldDump(bool has_owner_consent, bool UserCollector::HandleCrash(const std::string &crash_attributes, const char *force_exec) { CHECK(initialized_); - int pid = 0; + pid_t pid = 0; int signal = 0; + uid_t supplied_ruid = kUnknownUid; std::string kernel_supplied_name; - if (!ParseCrashAttributes(crash_attributes, &pid, &signal, + if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &supplied_ruid, &kernel_supplied_name)) { LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes; return false; @@ -681,7 +706,8 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, &reason); LOG(WARNING) << "Received crash notification for " << exec << "[" << pid - << "] sig " << signal << " (" << reason << ")"; + << "] sig " << signal << ", user " << supplied_ruid + << " (" << reason << ")"; if (dump) { count_crash_function_(); @@ -689,7 +715,7 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, if (generate_diagnostics_) { bool out_of_capacity = false; ErrorType error_type = - ConvertAndEnqueueCrash(pid, exec, &out_of_capacity); + ConvertAndEnqueueCrash(pid, exec, supplied_ruid, &out_of_capacity); if (error_type != kErrorNone) { if (!out_of_capacity) EnqueueCollectionErrorLog(pid, error_type, exec); diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 01ca2afd2..26f2b721c 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -104,7 +104,7 @@ class UserCollector : public CrashCollector { FilePath GetProcessPath(pid_t pid); bool GetSymlinkTarget(const FilePath &symlink, FilePath *target); - bool GetExecutableBaseNameFromPid(uid_t pid, + bool GetExecutableBaseNameFromPid(pid_t pid, std::string *base_name); // Returns, via |line|, the first line in |lines| that starts with |prefix|. // Returns true if a line is found, or false otherwise. @@ -149,7 +149,7 @@ class UserCollector : public CrashCollector { // and creates the directory if necessary with appropriate permissions. // Returns true whether or not directory needed to be created, false on // any failure. - bool GetCreatedCrashDirectory(pid_t pid, + bool GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid, FilePath *crash_file_path, bool *out_of_capacity); bool CopyStdinToCoreFile(const FilePath &core_path); @@ -161,10 +161,10 @@ class UserCollector : public CrashCollector { const FilePath &container_dir, const FilePath &core_path, const FilePath &minidump_path); - ErrorType ConvertAndEnqueueCrash(int pid, const std::string &exec_name, - bool *out_of_capacity); + ErrorType ConvertAndEnqueueCrash(pid_t pid, const std::string &exec_name, + uid_t supplied_ruid, bool *out_of_capacity); bool ParseCrashAttributes(const std::string &crash_attributes, - pid_t *pid, int *signal, + pid_t *pid, int *signal, uid_t *uid, std::string *kernel_supplied_name); bool ShouldDump(bool has_owner_consent, diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 290451292..305781c4b 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -66,7 +66,7 @@ class UserCollectorTest : public ::testing::Test { TEST_F(UserCollectorTest, EnableOK) { ASSERT_TRUE(collector_.Enable()); - ExpectFileEquals("|/my/path --user=%p:%s:%e", "test/core_pattern"); + ExpectFileEquals("|/my/path --user=%p:%s:%u:%e", "test/core_pattern"); ExpectFileEquals("4", "test/core_pipe_limit"); ASSERT_EQ(s_crashes, 0); EXPECT_TRUE(FindLog("Enabling user crash handling")); @@ -109,28 +109,36 @@ TEST_F(UserCollectorTest, DisableNoFileAccess) { TEST_F(UserCollectorTest, ParseCrashAttributes) { pid_t pid; int signal; + uid_t uid; std::string exec_name; - EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:foobar", - &pid, &signal, &exec_name)); + EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:foobar", + &pid, &signal, &uid, &exec_name)); EXPECT_EQ(123456, pid); EXPECT_EQ(11, signal); + EXPECT_EQ(1000, uid); EXPECT_EQ("foobar", exec_name); + EXPECT_TRUE(collector_.ParseCrashAttributes("4321:6:barfoo", + &pid, &signal, &uid, &exec_name)); + EXPECT_EQ(4321, pid); + EXPECT_EQ(6, signal); + EXPECT_EQ(-1, uid); + EXPECT_EQ("barfoo", exec_name); EXPECT_FALSE(collector_.ParseCrashAttributes("123456:11", - &pid, &signal, &exec_name)); + &pid, &signal, &uid, &exec_name)); EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:exec:extra", - &pid, &signal, &exec_name)); + &pid, &signal, &uid, &exec_name)); EXPECT_EQ("exec:extra", exec_name); EXPECT_FALSE(collector_.ParseCrashAttributes("12345p:11:foobar", - &pid, &signal, &exec_name)); + &pid, &signal, &uid, &exec_name)); EXPECT_FALSE(collector_.ParseCrashAttributes("123456:1 :foobar", - &pid, &signal, &exec_name)); + &pid, &signal, &uid, &exec_name)); EXPECT_FALSE(collector_.ParseCrashAttributes("123456::foobar", - &pid, &signal, &exec_name)); + &pid, &signal, &uid, &exec_name)); } TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) { From 570dd0ccae1ca812e3cd1289c414158f2cab9dda Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Wed, 6 Feb 2013 16:33:15 -0500 Subject: [PATCH 110/225] crash-reporter: Updating common.mk to ToT to enable profiling This update replaces the current common.mk used in this project with the newest version. This will allow all of the common.mk based projects to be on the same version for debugging and enables profiling support. BUG=chromium-os:37854 TEST=Exectuted the following commands to confirm the build works: MODE=profiling cros_workon_make --board=link MODE=profiling cros_workon_make --board=link --test cros_workon_make --board=link cros_workon_make --board=link --test Repeated these with emerge-link, USE=profiling, and FEATURES=test as need. For the emerge command with profiling and testing enable, confirmed the appropriate coverage files were created in /usr/share/profiling/... Change-Id: I12fcd4a590994223ddc42cf22670db82e94f0e03 Reviewed-on: https://gerrit.chromium.org/gerrit/42777 Tested-by: Ryan Harrison Reviewed-by: Mike Frysinger Commit-Queue: Ryan Harrison --- crash_reporter/common.mk | 122 ++++++++++++++++++++++++++------------- 1 file changed, 83 insertions(+), 39 deletions(-) diff --git a/crash_reporter/common.mk b/crash_reporter/common.mk index 5b36080d8..058e9063a 100644 --- a/crash_reporter/common.mk +++ b/crash_reporter/common.mk @@ -1,4 +1,4 @@ -# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # @@ -64,7 +64,11 @@ # Possible command line variables: # - COLOR=[0|1] to set ANSI color output (default: 1) # - VERBOSE=[0|1] to hide/show commands (default: 0) -# - MODE=dbg to turn down optimizations (default: opt) +# - MODE=[opt|dbg|profiling] (default: opt) +# opt - Enable optimizations for release builds +# dbg - Turn down optimization for debugging +# profiling - Turn off optimization and turn on profiling/coverage +# support. # - ARCH=[x86|arm|supported qemu name] (default: from portage or uname -m) # - SPLITDEBUG=[0|1] splits debug info in target.debug (default: 0) # If NOSTRIP=1, SPLITDEBUG will never strip the final emitted objects. @@ -97,8 +101,6 @@ COLOR ?= 1 VERBOSE ?= 0 MODE ?= opt ARCH ?= $(shell uname -m) -# TODO: profiling support not completed. -PROFILING ?= 0 NEEDS_ROOT = 0 NEEDS_MOUNTS = 0 @@ -161,8 +163,7 @@ MAKECMDGOALS ?= all _all:: %:: $(if $(filter 0,$(RUN_ONCE)), \ - $(QUIET)mkdir -p "$(OUT)" && \ - cd $(OUT) && \ + cd "$(OUT)" && \ $(MAKE) -r -I "$(SRC)" -f "$(CURDIR)/Makefile" \ SRC="$(CURDIR)" OUT="$(OUT)" $(foreach g,$(MAKECMDGOALS),"$(g)"),) $(eval RUN_ONCE := 1) @@ -182,10 +183,15 @@ ifeq ($(words $(filter-out Makefile common.mk %.d $(SRC)/Makefile \ # Helper macros # +# Create the directory if it doesn't yet exist. +define auto_mkdir + $(if $(wildcard $(dir $1)),$2,$(QUIET)mkdir -p "$(dir $1)") +endef + # Creates the actual archive with an index. # The target $@ must end with .pic.a or .pie.a. define update_archive - $(QUIET)mkdir -p "$(dir $(TARGET_OR_MEMBER))" + $(call auto_mkdir,$(TARGET_OR_MEMBER)) $(QUIET)# Create the archive in one step to avoid parallel use accessing it $(QUIET)# before all the symbols are present. @$(ECHO) "AR $(subst \ @@ -235,24 +241,34 @@ endef # Default variable values # -OBJCOPY ?= objcopy -STRIP ?= strip +# Only override toolchain vars if they are from make. +CROSS_COMPILE ?= +define override_var +ifneq ($(filter undefined default,$(origin $1)),) +$1 = $(CROSS_COMPILE)$2 +endif +endef +$(eval $(call override_var,AR,ar)) +$(eval $(call override_var,CC,gcc)) +$(eval $(call override_var,CXX,g++)) +$(eval $(call override_var,OBJCOPY,objcopy)) +$(eval $(call override_var,PKG_CONFIG,pkg-config)) +$(eval $(call override_var,RANLIB,ranlib)) +$(eval $(call override_var,STRIP,strip)) + RMDIR ?= rmdir -# Only override CC and CXX if they are from make. -ifeq ($(origin CC), default) - CC = gcc -endif -ifeq ($(origin CXX), default) - CXX = g++ -endif -ifeq ($(origin RANLIB), default) - RANLIB = ranlib -endif -RANLIB ?= ranlib ECHO = /bin/echo -e -ifeq ($(PROFILING),1) - $(warning PROFILING=1 disables relocatable executables.) +ifeq ($(lastword $(subst /, ,$(CC))),clang) +CDRIVER = clang +else +CDRIVER = gcc +endif + +ifeq ($(lastword $(subst /, ,$(CXX))),clang++) +CXXDRIVER = clang +else +CXXDRIVER = gcc endif # To update these from an including Makefile: @@ -260,17 +276,14 @@ endif # CXXFLAGS := -mahflag $(CXXFLAGS) # Prepend to the list # CXXFLAGS := $(filter-out badflag,$(CXXFLAGS)) # Filter out a value # The same goes for CFLAGS. -# TODO(wad) Moving to -fvisibility=internal by default would be nice too. -COMMON_CFLAGS := -Wall -Werror -fstack-protector-strong -fno-strict-aliasing \ - -ggdb3 -Wa,--noexecstack -O1 -fvisibility=internal -Wformat=2 -CXXFLAGS += $(COMMON_CFLAGS) -CFLAGS += $(COMMON_CFLAGS) +COMMON_CFLAGS-gcc := -fstack-protector-strong -fvisibility=internal -ggdb3 \ + -Wa,--noexecstack +COMMON_CFLAGS-clang := -fstack-protector-all -fvisibility=hidden -ggdb +COMMON_CFLAGS := -Wall -Werror -fno-strict-aliasing -O1 -Wformat=2 +CXXFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CXXDRIVER)) +CFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CDRIVER)) CPPFLAGS += -D_FORTIFY_SOURCE=2 -ifeq ($(PROFILING),1) - CFLAGS := -pg - CXXFLAGS := -pg -endif ifeq ($(MODE),opt) # Up the optimizations. @@ -284,6 +297,12 @@ ifeq ($(MODE),opt) endif endif +ifeq ($(MODE),profiling) + CFLAGS := $(CFLAGS) -O0 -g --coverage + CXXFLAGS := $(CXXFLAGS) -O0 -g --coverage + LDFLAGS := $(LDFLAGS) --coverage +endif + LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,noexecstack -Wl,-z,now # Fancy helpers for color if a prompt is defined @@ -323,7 +342,7 @@ TARGET_OR_MEMBER = $(lastword $(subst $(LP), ,$(subst $(RP),,$(or $%,$@)))) # all non-.o files. define COMPILE_BINARY_implementation @$(ECHO) "LD$(1) $(subst $(PWD)/,,$(TARGET_OR_MEMBER))" - $(QUIET)mkdir -p "$(dir $(TARGET_OR_MEMBER))" + $(call auto_mkdir,$(TARGET_OR_MEMBER)) $(QUIET)$($(1)) $(COMPILE_PIE_FLAGS) -o $(TARGET_OR_MEMBER) \ $(2) $(LDFLAGS) \ $(filter %.o %.a,$(^:.o=.pie.o)) \ @@ -347,7 +366,7 @@ endef COMMA := , define COMPILE_LIBRARY_implementation @$(ECHO) "SHARED$(1) $(subst $(PWD)/,,$(TARGET_OR_MEMBER))" - $(QUIET)mkdir -p "$(dir $(TARGET_OR_MEMBER))" + $(call auto_mkdir,$(TARGET_OR_MEMBER)) $(QUIET)$($(1)) -shared -Wl,-E -o $(TARGET_OR_MEMBER) \ $(2) $(LDFLAGS) \ $(if $(filter %.a,$^),-Wl$(COMMA)--whole-archive,) \ @@ -502,30 +521,30 @@ CXX_OBJECTS = $(patsubst $(SRC)/%.cc,%.o,$(wildcard $(SRC)/*.cc)) # $(5) source dir: _only_ if $(SRC). Leave blank for obj tree. define add_object_rules $(patsubst %.o,%.pie.o,$(1)): %.pie.o: $(5)%.$(3) %.o.depends - $$(QUIET)mkdir -p "$$(dir $$@)" + $$(call auto_mkdir,$$@) $$(call OBJECT_PATTERN_implementation,$(2),\ $$(basename $$@),$$($(4)) $$(CPPFLAGS) $$(OBJ_PIE_FLAG)) $(patsubst %.o,%.pic.o,$(1)): %.pic.o: $(5)%.$(3) %.o.depends - $$(QUIET)mkdir -p "$$(dir $$@)" + $$(call auto_mkdir,$$@) $$(call OBJECT_PATTERN_implementation,$(2),\ $$(basename $$@),$$($(4)) $$(CPPFLAGS) -fPIC) # Placeholder for depends $(patsubst %.o,%.o.depends,$(1)): - $$(QUIET)mkdir -p "$$(dir $$@)" + $$(call auto_mkdir,$$@) $$(QUIET)touch "$$@" $(1): %.o: %.pic.o %.pie.o - $$(QUIET)mkdir -p "$$(dir $$@)" + $$(call auto_mkdir,$$@) $$(QUIET)touch "$$@" endef define OBJECT_PATTERN_implementation @$(ECHO) "$(1) $(subst $(SRC)/,,$<) -> $(2).o" - $(QUIET)mkdir -p "$(dir $(2))" + $(call auto_mkdir,$@) $(QUIET)$($(1)) -c -MD -MF $(2).d $(3) -o $(2).o $< - $(QUIET)# Wrap all the deps in $(wildcard) so a missing header + $(QUIET)# Wrap all the deps in $$(wildcard) so a missing header $(QUIET)# won't cause weirdness. First we remove newlines and \, $(QUIET)# then wrap it. $(QUIET)sed -i -e :j -e '$$!N;s|\\\s*\n| |;tj' \ @@ -628,7 +647,27 @@ all: # Builds and runs tests for the target arch # Run them in parallel +# After the test have completed, if profiling, run coverage analysis tests: +ifeq ($(MODE),profiling) + @$(ECHO) -n "COVERAGE gcov " + @$(ECHO) "[$(COLOR_YELLOW)STARTED$(COLOR_RESET)]" + $(QUIET)(FILES=""; \ + for GCNO in `find . -name "*.gcno"`; \ + do \ + GCDA="$${GCNO%.gcno}.gcda"; \ + [ -e $${GCDA} ] && FILES="$${FILES} $${GCDA}"; \ + done; \ + gcov -l $${FILES}) + @$(ECHO) -n "COVERAGE gcov " + @$(ECHO) "[$(COLOR_YELLOW)FINISHED$(COLOR_RESET)]" + @$(ECHO) -n "COVERAGE lcov " + @$(ECHO) "[$(COLOR_YELLOW)STARTED$(COLOR_RESET)]" + $(QUIET)lcov --capture --directory . --output-file=lcov-coverage.info + $(QUIET)genhtml lcov-coverage.info --output-directory lcov-html + @$(ECHO) -n "COVERAGE lcov " + @$(ECHO) "[$(COLOR_YELLOW)FINISHED$(COLOR_RESET)]" +endif .PHONY: tests qemu_clean: @@ -733,6 +772,8 @@ endef clean: qemu_clean clean: CLEAN($(OUT)*.d) CLEAN($(OUT)*.o) CLEAN($(OUT)*.debug) clean: CLEAN($(OUT)*.test) CLEAN($(OUT)*.depends) +clean: CLEAN($(OUT)*.gcno) CLEAN($(OUT)*.gcda) CLEAN($(OUT)*.gcov) +clean: CLEAN($(OUT)lcov-coverage.info) CLEAN($(OUT)lcov-html) clean: $(QUIET)# Always delete the containing directory last. @@ -776,6 +817,9 @@ $(eval LD_DIRS := $(LD_DIRS):$(OUT)$(MODULE)) clean: CLEAN($(OUT)$(MODULE)/*.d) CLEAN($(OUT)$(MODULE)/*.o) clean: CLEAN($(OUT)$(MODULE)/*.debug) CLEAN($(OUT)$(MODULE)/*.test) clean: CLEAN($(OUT)$(MODULE)/*.depends) +clean: CLEAN($(OUT)$(MODULE)/*.gcno) CLEAN($(OUT)$(MODULE)/*.gcda) +clean: CLEAN($(OUT)$(MODULE)/*.gcov) CLEAN($(OUT)lcov-coverage.info) +clean: CLEAN($(OUT)lcov-html) $(info + submodule: $(MODULE_NAME)) # We must eval otherwise they may be dropped. From 9a7ce9fbef026610c86af15c76af2d8b92654412 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Sat, 19 Jan 2013 11:03:37 -0500 Subject: [PATCH 111/225] use volatile markings in crash test This makes it work with clang (supposedly). BUG=chromium-os:37965 TEST=`cros_run_unit_tests --board=stumpy -p crash-reporter` passes Change-Id: I598df1be7c8a38e14cde5f5328a3b6e2e1532ea7 Reviewed-on: https://gerrit.chromium.org/gerrit/43377 Reviewed-by: Ben Chan Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_reporter.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index de67326b4..401448e7f 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -148,7 +148,7 @@ static int HandleUserCrash(UserCollector *user_collector) { // Make it possible to test what happens when we crash while // handling a crash. if (FLAGS_crash_test) { - *(char *)0 = 0; + *(volatile char *)0 = 0; return 0; } From 1a8780d309b980914a5e2403bf9addafe07386ec Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 14 Feb 2013 22:39:57 -0500 Subject: [PATCH 112/225] update to libchrome 180609 Purely mechanical changes here. BUG=chromium-os:38933 TEST=`cros_run_unit_tests --board=stumpy -p crash-reporter` passes CQ-DEPEND=CL:43376 Change-Id: I62d5d12556517bedf48d36ada4c651ff1c671fe9 Reviewed-on: https://gerrit.chromium.org/gerrit/43378 Reviewed-by: Mike Frysinger Tested-by: Mike Frysinger Commit-Queue: Mike Frysinger --- crash_reporter/crash_collector.cc | 2 +- crash_reporter/list_proxies.cc | 4 ++-- crash_reporter/udev_collector_test.cc | 6 +++--- crash_reporter/user_collector.cc | 1 + crash_reporter/user_collector_test.cc | 6 +++--- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 35d2e68f5..d6fb3a4f0 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -16,9 +16,9 @@ #include #include -#include "base/eintr_wrapper.h" #include "base/file_util.h" #include "base/logging.h" +#include "base/posix/eintr_wrapper.h" #include "base/string_split.h" #include "base/string_util.h" #include "base/stringprintf.h" diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc index e895058be..9f3b3ece3 100644 --- a/crash_reporter/list_proxies.cc +++ b/crash_reporter/list_proxies.cc @@ -12,8 +12,8 @@ #include "base/command_line.h" #include "base/file_util.h" -#include "base/string_tokenizer.h" #include "base/string_util.h" +#include "base/strings/string_tokenizer.h" #include "base/values.h" #include "chromeos/dbus/dbus.h" #include "chromeos/syslog_logging.h" @@ -51,7 +51,7 @@ std::deque ParseProxyString(const std::string &input) { // Some of this code taken from // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc - StringTokenizer entry_tok(input, ";"); + base::StringTokenizer entry_tok(input, ";"); while (entry_tok.GetNext()) { std::string token = entry_tok.token(); TrimWhitespaceASCII(token, TRIM_ALL, &token); diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc index b75abfde5..4d2bbe6db 100644 --- a/crash_reporter/udev_collector_test.cc +++ b/crash_reporter/udev_collector_test.cc @@ -3,8 +3,8 @@ // found in the LICENSE file. #include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" #include "base/memory/scoped_ptr.h" -#include "base/scoped_temp_dir.h" #include "chromeos/test_helpers.h" #include "crash-reporter/udev_collector.h" #include "gtest/gtest.h" @@ -51,7 +51,7 @@ class UdevCollectorTest : public ::testing::Test { collector_.reset(new UdevCollector); collector_->Initialize(CountCrash, IsMetrics); - temp_dir_generator_.reset(new ScopedTempDir()); + temp_dir_generator_.reset(new base::ScopedTempDir()); ASSERT_TRUE(temp_dir_generator_->CreateUniqueTempDir()); EXPECT_TRUE(temp_dir_generator_->IsValid()); @@ -72,7 +72,7 @@ class UdevCollectorTest : public ::testing::Test { protected: scoped_ptr collector_; - scoped_ptr temp_dir_generator_; + scoped_ptr temp_dir_generator_; }; TEST_F(UdevCollectorTest, TestNoConsent) { diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index cc4beead2..26c7dc1ea 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -17,6 +17,7 @@ #include "base/file_util.h" #include "base/logging.h" +#include "base/posix/eintr_wrapper.h" #include "base/stl_util.h" #include "base/string_split.h" #include "base/string_util.h" diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 305781c4b..4c919ded8 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -7,8 +7,8 @@ #include #include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" #include "base/string_split.h" -#include "base/scoped_temp_dir.h" #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/user_collector.h" @@ -464,7 +464,7 @@ TEST_F(UserCollectorTest, CopyOffProcFilesOK) { } TEST_F(UserCollectorTest, ValidateProcFiles) { - ScopedTempDir temp_dir; + base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); FilePath container_dir = temp_dir.path(); @@ -485,7 +485,7 @@ TEST_F(UserCollectorTest, ValidateProcFiles) { } TEST_F(UserCollectorTest, ValidateCoreFile) { - ScopedTempDir temp_dir; + base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); FilePath container_dir = temp_dir.path(); FilePath core_file = container_dir.Append("core"); From 9f90acaa4d420530d7b4ddd37112518df68e373a Mon Sep 17 00:00:00 2001 From: Simon Que Date: Tue, 19 Feb 2013 17:19:52 -0800 Subject: [PATCH 113/225] Replace FilePath declaration with file_path.h include Fixes a clang error. Also use "base::FilePath" since FilePath has been moved to the base namespace. BUG=chromium-os:39046 TEST=The following command passes: USE="chrome_internal" CFLAGS="-clang -print-cmdline" CXXFLAGS="-clang -print-cmdline" emerge-x86-alex crash-reporter Change-Id: I7da82093c5685b5b556cba971b1e1b14ac0f59c4 Signed-off-by: Simon Que Reviewed-on: https://gerrit.chromium.org/gerrit/43577 Reviewed-by: Ben Chan --- crash_reporter/crash_collector.cc | 2 ++ crash_reporter/crash_collector.h | 24 +++++++------- crash_reporter/crash_collector_test.cc | 1 + crash_reporter/crash_reporter.cc | 3 +- crash_reporter/kernel_collector.cc | 2 ++ crash_reporter/kernel_collector.h | 8 ++--- crash_reporter/kernel_collector_test.cc | 1 + crash_reporter/udev_collector.cc | 2 ++ crash_reporter/udev_collector.h | 5 ++- crash_reporter/udev_collector_test.cc | 2 ++ crash_reporter/unclean_shutdown_collector.cc | 2 ++ crash_reporter/unclean_shutdown_collector.h | 6 ++-- .../unclean_shutdown_collector_test.cc | 1 + crash_reporter/user_collector.cc | 2 ++ crash_reporter/user_collector.h | 32 +++++++++---------- crash_reporter/user_collector_test.cc | 1 + 16 files changed, 54 insertions(+), 40 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index d6fb3a4f0..990ef034f 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -54,6 +54,8 @@ static const uid_t kRootGroup = 0; // number of core files or minidumps reaches this number. const int CrashCollector::kMaxCrashDirectorySize = 32; +using base::FilePath; + CrashCollector::CrashCollector() : forced_crash_directory_(NULL), lsb_release_(kLsbRelease), diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 650f5366a..3f0a27d99 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -60,7 +60,7 @@ class CrashCollector { // Writes |data| of |size| to |filename|, which must be a new file. // If the file already exists or writing fails, return a negative value. // Otherwise returns the number of bytes written. - int WriteNewFile(const FilePath &filename, const char *data, int size); + int WriteNewFile(const base::FilePath &filename, const char *data, int size); // Return a filename that has only [a-z0-1_] characters by mapping // all others into '_'. @@ -72,7 +72,7 @@ class CrashCollector { forced_crash_directory_ = forced_directory; } - FilePath GetCrashDirectoryInfo(uid_t process_euid, + base::FilePath GetCrashDirectoryInfo(uid_t process_euid, uid_t default_user_id, gid_t default_user_group, mode_t *mode, @@ -88,7 +88,7 @@ class CrashCollector { // true whether or not directory needed to be created, false on any // failure. If the crash directory is at capacity, returns false. bool GetCreatedCrashDirectoryByEuid(uid_t euid, - FilePath *crash_file_path, + base::FilePath *crash_file_path, bool *out_of_capacity); // Format crash name based on components. @@ -98,28 +98,28 @@ class CrashCollector { // Create a file path to a file in |crash_directory| with the given // |basename| and |extension|. - FilePath GetCrashPath(const FilePath &crash_directory, - const std::string &basename, - const std::string &extension); + base::FilePath GetCrashPath(const base::FilePath &crash_directory, + const std::string &basename, + const std::string &extension); // Check given crash directory still has remaining capacity for another // crash. - bool CheckHasCapacity(const FilePath &crash_directory); + bool CheckHasCapacity(const base::FilePath &crash_directory); // Checks if the line starts with '#' after optional whitespace. static bool IsCommentLine(const std::string &line); // Read the given file of form [\n...] and return // a map of its contents. - bool ReadKeyValueFile(const FilePath &file, + bool ReadKeyValueFile(const base::FilePath &file, char separator, std::map *dictionary); // Write a log applicable to |exec_name| to |output_file| based on the // log configuration file at |config_path|. - bool GetLogContents(const FilePath &config_path, + bool GetLogContents(const base::FilePath &config_path, const std::string &exec_name, - const FilePath &output_file); + const base::FilePath &output_file); // Add non-standard meta data to the crash metadata file. Call // before calling WriteCrashMetaData. Key must not contain "=" or @@ -127,7 +127,7 @@ class CrashCollector { void AddCrashMetaData(const std::string &key, const std::string &value); // Write a file of metadata about crash. - void WriteCrashMetaData(const FilePath &meta_path, + void WriteCrashMetaData(const base::FilePath &meta_path, const std::string &exec_name, const std::string &payload_path); @@ -146,7 +146,7 @@ class CrashCollector { std::string extra_metadata_; const char *forced_crash_directory_; const char *lsb_release_; - FilePath log_config_path_; + base::FilePath log_config_path_; }; #endif // _CRASH_REPORTER_CRASH_COLLECTOR_H_ diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 2635bbfcc..fff22b53f 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -19,6 +19,7 @@ static const char kBinCp[] = "/bin/cp"; static const char kBinEcho[] = "/bin/echo"; static const char kBinFalse[] = "/bin/false"; +using base::FilePath; using chromeos::FindLog; void CountCrash() { diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 401448e7f..e76c5c82f 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -41,7 +41,6 @@ static const char kKernelCrashDetected[] = "/var/run/kernel-crash-detected"; static const char kUncleanShutdownDetected[] = "/var/run/unclean-shutdown-detected"; - // Enumeration of kinds of crashes to be used in the CrashCounter histogram. enum CrashKinds { kCrashKindUncleanShutdown = 1, @@ -53,6 +52,8 @@ enum CrashKinds { static MetricsLibrary s_metrics_lib; +using base::FilePath; + static bool IsFeedbackAllowed() { return s_metrics_lib.AreMetricsEnabled(); } diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 83eca8a16..579014001 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -45,6 +45,8 @@ static const char *s_pc_regex[] = { " RIP \\[<.*>\\] ([^\\+ ]+).*", // X86_64 uses RIP for the program counter }; +using base::FilePath; + COMPILE_ASSERT(arraysize(s_pc_regex) == KernelCollector::archCount, missing_arch_pc_regexp); diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index b4f3c1a70..17b55a0fe 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -13,8 +13,6 @@ #include "crash-reporter/crash_collector.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST -class FilePath; - // Kernel crash collector. class KernelCollector : public CrashCollector { public: @@ -32,7 +30,7 @@ class KernelCollector : public CrashCollector { virtual ~KernelCollector(); - void OverridePreservedDumpPath(const FilePath &file_path); + void OverridePreservedDumpPath(const base::FilePath &file_path); // Enable collection. bool Enable(); @@ -66,7 +64,7 @@ class KernelCollector : public CrashCollector { bool LoadPreservedDump(std::string *contents); void StripSensitiveData(std::string *kernel_dump); - void GetRamoopsRecordPath(FilePath *path, size_t record); + void GetRamoopsRecordPath(base::FilePath *path, size_t record); virtual bool LoadParameters(); bool HasMoreRecords(); @@ -98,7 +96,7 @@ class KernelCollector : public CrashCollector { enum ArchKind GetCompilerArch(void); bool is_enabled_; - FilePath ramoops_dump_path_; + base::FilePath ramoops_dump_path_; size_t records_; // The architecture of kernel dump strings we are working with. diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index ee96f058e..2dc50b403 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -19,6 +19,7 @@ static bool s_metrics = false; static const char kTestKCrash[] = "test/kcrash"; static const char kTestCrashDirectory[] = "test/crash_directory"; +using base::FilePath; using chromeos::FindLog; using chromeos::GetLog; diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc index 138ec4bda..4e4b21340 100644 --- a/crash_reporter/udev_collector.cc +++ b/crash_reporter/udev_collector.cc @@ -16,6 +16,8 @@ static const char kGzipPath[] = "/bin/gzip"; static const char kUdevExecName[] = "udev"; static const char kUdevSignatureKey[] = "sig"; +using base::FilePath; + UdevCollector::UdevCollector() {} UdevCollector::~UdevCollector() {} diff --git a/crash_reporter/udev_collector.h b/crash_reporter/udev_collector.h index acb09cba0..29c19ef78 100644 --- a/crash_reporter/udev_collector.h +++ b/crash_reporter/udev_collector.h @@ -7,11 +7,10 @@ #include +#include "base/file_path.h" #include "crash-reporter/crash_collector.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST -class FilePath; - // Udev crash collector. class UdevCollector : public CrashCollector { public: @@ -30,7 +29,7 @@ class UdevCollector : public CrashCollector { // Mutator for unit testing. void set_log_config_path(const std::string& path) { - log_config_path_ = FilePath(path); + log_config_path_ = base::FilePath(path); } }; diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc index 4d2bbe6db..31fdbf4f7 100644 --- a/crash_reporter/udev_collector_test.cc +++ b/crash_reporter/udev_collector_test.cc @@ -9,6 +9,8 @@ #include "crash-reporter/udev_collector.h" #include "gtest/gtest.h" +using base::FilePath; + namespace { // Dummy log config file name. diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc index ad7386f26..b1462ad37 100644 --- a/crash_reporter/unclean_shutdown_collector.cc +++ b/crash_reporter/unclean_shutdown_collector.cc @@ -17,6 +17,8 @@ static const char kPowerdSuspended[] = "powerd_suspended"; // Presence of this file indicates that the battery was critically low. static const char kPowerdLowBattery[] = "powerd_low_battery"; +using base::FilePath; + UncleanShutdownCollector::UncleanShutdownCollector() : unclean_shutdown_file_(kUncleanShutdownFile), powerd_trace_path_(kPowerdTracePath), diff --git a/crash_reporter/unclean_shutdown_collector.h b/crash_reporter/unclean_shutdown_collector.h index 7e10e7f28..37eb4dffb 100644 --- a/crash_reporter/unclean_shutdown_collector.h +++ b/crash_reporter/unclean_shutdown_collector.h @@ -40,9 +40,9 @@ class UncleanShutdownCollector : public CrashCollector { bool DeadBatteryCausedUncleanShutdown(); const char *unclean_shutdown_file_; - FilePath powerd_trace_path_; - FilePath powerd_suspended_file_; - FilePath powerd_low_battery_file_; + base::FilePath powerd_trace_path_; + base::FilePath powerd_suspended_file_; + base::FilePath powerd_low_battery_file_; }; #endif // _CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_ diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc index 8adb4d384..1bb0c9431 100644 --- a/crash_reporter/unclean_shutdown_collector_test.cc +++ b/crash_reporter/unclean_shutdown_collector_test.cc @@ -20,6 +20,7 @@ static const char kTestLowBattery[] = "test/low_battery"; static const char kTestSuspended[] = "test/suspended"; static const char kTestUnclean[] = "test/unclean"; +using base::FilePath; using ::chromeos::FindLog; void CountCrash() { diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 26c7dc1ea..a89ea8169 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -53,6 +53,8 @@ static const uid_t kUnknownUid = -1; const char *UserCollector::kUserId = "Uid:\t"; const char *UserCollector::kGroupId = "Gid:\t"; +using base::FilePath; + UserCollector::UserCollector() : generate_diagnostics_(false), core_pattern_file_(kCorePatternFile), diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 26f2b721c..c7ac0de50 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -8,10 +8,10 @@ #include #include +#include "base/file_path.h" #include "crash-reporter/crash_collector.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST -class FilePath; class SystemLogging; // User crash collector. @@ -101,9 +101,9 @@ class UserCollector : public CrashCollector { std::string GetPattern(bool enabled) const; bool SetUpInternal(bool enabled); - FilePath GetProcessPath(pid_t pid); - bool GetSymlinkTarget(const FilePath &symlink, - FilePath *target); + base::FilePath GetProcessPath(pid_t pid); + bool GetSymlinkTarget(const base::FilePath &symlink, + base::FilePath *target); bool GetExecutableBaseNameFromPid(pid_t pid, std::string *base_name); // Returns, via |line|, the first line in |lines| that starts with |prefix|. @@ -129,38 +129,38 @@ class UserCollector : public CrashCollector { void EnqueueCollectionErrorLog(pid_t pid, ErrorType error_type, const std::string &exec_name); - bool CopyOffProcFiles(pid_t pid, const FilePath &container_dir); + bool CopyOffProcFiles(pid_t pid, const base::FilePath &container_dir); // Validates the proc files at |container_dir| and returns true if they // are usable for the core-to-minidump conversion later. For instance, if // a process is reaped by the kernel before the copying of its proc files // takes place, some proc files like /proc//maps may contain nothing // and thus become unusable. - bool ValidateProcFiles(const FilePath &container_dir) const; + bool ValidateProcFiles(const base::FilePath &container_dir) const; // Validates the core file at |core_path| and returns kErrorNone if // the file contains the ELF magic bytes and an ELF class that matches the // platform (i.e. 32-bit ELF on a 32-bit platform or 64-bit ELF on a 64-bit // platform), which is due to the limitation in core2md. It returns an error // type otherwise. - ErrorType ValidateCoreFile(const FilePath &core_path) const; + ErrorType ValidateCoreFile(const base::FilePath &core_path) const; // Determines the crash directory for given pid based on pid's owner, // and creates the directory if necessary with appropriate permissions. // Returns true whether or not directory needed to be created, false on // any failure. bool GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid, - FilePath *crash_file_path, + base::FilePath *crash_file_path, bool *out_of_capacity); - bool CopyStdinToCoreFile(const FilePath &core_path); - bool RunCoreToMinidump(const FilePath &core_path, - const FilePath &procfs_directory, - const FilePath &minidump_path, - const FilePath &temp_directory); + bool CopyStdinToCoreFile(const base::FilePath &core_path); + bool RunCoreToMinidump(const base::FilePath &core_path, + const base::FilePath &procfs_directory, + const base::FilePath &minidump_path, + const base::FilePath &temp_directory); ErrorType ConvertCoreToMinidump(pid_t pid, - const FilePath &container_dir, - const FilePath &core_path, - const FilePath &minidump_path); + const base::FilePath &container_dir, + const base::FilePath &core_path, + const base::FilePath &minidump_path); ErrorType ConvertAndEnqueueCrash(pid_t pid, const std::string &exec_name, uid_t supplied_ruid, bool *out_of_capacity); bool ParseCrashAttributes(const std::string &crash_attributes, diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 4c919ded8..d1a3bcf04 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -20,6 +20,7 @@ static bool s_metrics = false; static const char kFilePath[] = "/my/path"; +using base::FilePath; using chromeos::FindLog; void CountCrash() { From 6fdc0b48cb5e262083c06a6b1c7595c01b4b2e74 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Thu, 11 Apr 2013 17:22:13 -0700 Subject: [PATCH 114/225] Collect and report kernel warnings. A flex-based daemon collects kernel warnings by parsing /var/log/messages as lines are added to it. For every warning the daemon sends a sample to a sparse UMA histogram using a hash of the warning as the bucket. Then, if the warning hasn't been seen before, the daemon invokes the crash collector to upload the warning stack trace. BUG=chromium:217382 TEST=manually tested (for now), automated test on its way CQ-DEPEND=Ic8d5773d05d717a275c4a4b5616e0e4c307337b8 CQ-DEPEND=I6a4010acad0ffe20c702bb0fc455e3da7cdf3ac1 Change-Id: I89090e5c2b61ec46b4e740f0895c591728d70e77 Reviewed-on: https://gerrit.chromium.org/gerrit/48277 Commit-Queue: Luigi Semenzato Reviewed-by: Luigi Semenzato Tested-by: Luigi Semenzato --- crash_reporter/Makefile | 17 +- crash_reporter/TEST_WARNING | 31 ++ crash_reporter/crash_reporter.cc | 21 ++ crash_reporter/kernel_warning_collector.cc | 97 ++++++ crash_reporter/kernel_warning_collector.h | 31 ++ crash_reporter/warn_collector.l | 300 ++++++++++++++++++ crash_reporter/warn_collector_test.c | 14 + crash_reporter/warn_collector_test.sh | 73 +++++ .../warn_collector_test_reporter.sh | 16 + 9 files changed, 599 insertions(+), 1 deletion(-) create mode 100644 crash_reporter/TEST_WARNING create mode 100644 crash_reporter/kernel_warning_collector.cc create mode 100644 crash_reporter/kernel_warning_collector.h create mode 100644 crash_reporter/warn_collector.l create mode 100644 crash_reporter/warn_collector_test.c create mode 100755 crash_reporter/warn_collector_test.sh create mode 100755 crash_reporter/warn_collector_test_reporter.sh diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index 648308f6f..4a0f488f6 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -12,6 +12,7 @@ PKG_CONFIG ?= pkg-config CRASH_OBJS = \ crash_collector.o \ kernel_collector.o \ + kernel_warning_collector.o \ udev_collector.o \ unclean_shutdown_collector.o \ user_collector.o @@ -44,6 +45,18 @@ clean: CLEAN(list_proxies) all: CXX_BINARY(list_proxies) +# -- Executable warn_collector -- + +CC_BINARY(warn_collector): LDLIBS += -lfl -lmetrics +CC_BINARY(warn_collector): lex.yy.o +clean: CLEAN(warn_collector) +all: CC_BINARY(warn_collector) +lex.yy.c: $(SRC)/warn_collector.l + flex $< +clean: CLEAN(lex.yy.o) +$(eval $(call add_object_rules,lex.yy.o,CC,c)) + + # -- Unit Tests -- # Uncomment these to just run specific test(s). @@ -65,8 +78,10 @@ clean: CLEAN($(1)) tests: TEST(CXX_BINARY($(1))) endef +CC_BINARY(warn_collector_test): warn_collector_test.o + # If $(TEST_BINS) is not already set, assume any filename matching *_test.cc # is a test. TEST_BINS ?= $(patsubst $(SRC)/%.cc,%,$(wildcard $(SRC)/*_test.cc)) $(foreach prog,$(TEST_BINS),$(eval $(call TEST_RULES_template,$(prog)))) - +tests: TEST(CC_BINARY(warn_collector_test)) diff --git a/crash_reporter/TEST_WARNING b/crash_reporter/TEST_WARNING new file mode 100644 index 000000000..64ad2e9a0 --- /dev/null +++ b/crash_reporter/TEST_WARNING @@ -0,0 +1,31 @@ +Apr 31 25:25:25 localhost kernel: [117959.226729] [] do_vfs_ioctl+0x469/0x4b3 +Apr 31 25:25:25 localhost kernel: [117959.226738] [] ? fsnotify_access+0x58/0x60 +Apr 31 25:25:25 localhost kernel: [117959.226747] [] ? vfs_read+0xad/0xd7 +Apr 31 25:25:25 localhost kernel: [117959.226756] [] sys_ioctl+0x56/0x7b +Apr 31 25:25:25 localhost kernel: [117959.226765] [] ? sys_read+0x43/0x73 +Apr 31 25:25:25 localhost kernel: [117959.226774] [] system_call_fastpath+0x16/0x1b +Apr 31 25:25:25 localhost kernel: [117959.226782] ---[ end trace f16822cad7406cec ]--- +Apr 31 25:25:25 localhost kernel: [117959.231085] ------------[ cut here ]------------ +Apr 31 25:25:25 localhost kernel: [117959.231100] WARNING: at /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9() +Apr 31 25:25:25 localhost kernel: [117959.231113] Hardware name: Link +Apr 31 25:25:25 localhost kernel: [117959.231117] eDP powered off while attempting aux channel communication. +Apr 31 25:25:25 localhost kernel: [117959.231240] Pid: 10508, comm: X Tainted: G WC 3.4.0 #1 +Apr 31 25:25:25 localhost kernel: [117959.231247] Call Trace: +Apr 31 25:25:25 localhost kernel: [117959.231393] [] ? fsnotify_access+0x58/0x60 +Apr 31 25:25:25 localhost kernel: [117959.231402] [] ? vfs_read+0xad/0xd7 +Apr 31 25:25:25 localhost kernel: [117959.231411] [] sys_ioctl+0x56/0x7b +Apr 31 25:25:25 localhost kernel: [117959.231420] [] ? sys_read+0x43/0x73 +Apr 31 25:25:25 localhost kernel: [117959.231431] [] system_call_fastpath+0x16/0x1b +Apr 31 25:25:25 localhost kernel: [117959.231439] ---[ end trace f16822cad7406ced ]--- +Apr 31 25:25:25 localhost kernel: [117959.231450] ------------[ cut here ]------------ +Apr 31 25:25:25 localhost kernel: [117959.231458] BARNING: at /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9() +Apr 31 25:25:25 localhost kernel: [117959.231458] ("BARNING" above is intentional) +Apr 31 25:25:25 localhost kernel: [117959.231471] Hardware name: Link +Apr 31 25:25:25 localhost kernel: [117959.231475] eDP powered off while attempting aux channel communication. +Apr 31 25:25:25 localhost kernel: [117959.231482] Modules linked in: nls_iso8859_1 nls_cp437 vfat fat rfcomm i2c_dev ath9k_btcoex snd_hda_codec_hdmi snd_hda_codec_ca0132 mac80211 snd_hda_intel ath9k_common_btcoex snd_hda_codec ath9k_hw_btcoex aesni_intel cryptd snd_hwdep ath snd_pcm aes_x86_64 isl29018(C) memconsole snd_timer snd_page_alloc industrialio(C) cfg80211 rtc_cmos nm10_gpio zram(C) zsmalloc(C) lzo_decompress lzo_compress fuse nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_filter ip6_tables xt_mark option usb_wwan cdc_ether usbnet ath3k btusb bluetooth uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops joydev +Apr 31 25:25:25 localhost kernel: [117959.231588] Pid: 10508, comm: X Tainted: G WC 3.4.0 #1 +Apr 31 25:25:25 localhost kernel: [117959.231595] Call Trace: +Apr 31 25:25:25 localhost kernel: [117959.231601] [] warn_slowpath_common+0x83/0x9c +Apr 31 25:25:25 localhost kernel: [117959.231610] [] warn_slowpath_fmt+0x46/0x48 +Apr 31 25:25:25 localhost kernel: [117959.231620] [] intel_dp_check_edp+0x6b/0xb9 +Apr 31 25:25:25 localhost kernel: [117959.231629] [] ? warn_slowpath_fmt+ diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index e76c5c82f..d298108e5 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -15,6 +15,7 @@ #include "base/stringprintf.h" #include "chromeos/syslog_logging.h" #include "crash-reporter/kernel_collector.h" +#include "crash-reporter/kernel_warning_collector.h" #include "crash-reporter/udev_collector.h" #include "crash-reporter/unclean_shutdown_collector.h" #include "crash-reporter/user_collector.h" @@ -30,6 +31,7 @@ DEFINE_bool(crash_test, false, "Crash test"); DEFINE_string(user, "", "User crash info (pid:signal:exec_name)"); DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); DEFINE_string(udev, "", "Udev event description (type:device:subsystem)"); +DEFINE_bool(kernel_warning, false, "Report collected kernel warning"); #pragma GCC diagnostic error "-Wstrict-aliasing" static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; @@ -47,6 +49,7 @@ enum CrashKinds { kCrashKindUser = 2, kCrashKindKernel = 3, kCrashKindUdev = 4, + kCrashKindKernelWarning = 5, kCrashKindMax }; @@ -176,6 +179,17 @@ static int HandleUdevCrash(UdevCollector *udev_collector) { return 0; } +static int HandleKernelWarning(KernelWarningCollector + *kernel_warning_collector) { + // Accumulate logs to help in diagnosing failures during collection. + chromeos::LogToString(true); + bool handled = kernel_warning_collector->Collect(); + chromeos::LogToString(false); + if (!handled) + return 1; + return 0; +} + // Interactive/diagnostics mode for generating kernel crash signatures. static int GenerateKernelSignature(KernelCollector *kernel_collector) { std::string kcrash_contents; @@ -238,6 +252,9 @@ int main(int argc, char *argv[]) { UdevCollector udev_collector; udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed); + KernelWarningCollector kernel_warning_collector; + udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed); + if (FLAGS_init) { return Initialize(&kernel_collector, &user_collector, @@ -258,5 +275,9 @@ int main(int argc, char *argv[]) { return HandleUdevCrash(&udev_collector); } + if (FLAGS_kernel_warning) { + return HandleKernelWarning(&kernel_warning_collector); + } + return HandleUserCrash(&user_collector); } diff --git a/crash_reporter/kernel_warning_collector.cc b/crash_reporter/kernel_warning_collector.cc new file mode 100644 index 000000000..b504f497b --- /dev/null +++ b/crash_reporter/kernel_warning_collector.cc @@ -0,0 +1,97 @@ +// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crash-reporter/kernel_warning_collector.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/stringprintf.h" + +namespace { +const char kExecName[] = "kernel-warning"; +const char kKernelWarningSignatureKey[] = "sig"; +const char kKernelWarningPath[] = "/var/run/kwarn/warning"; +const pid_t kKernelPid = 0; +const uid_t kRootUid = 0; +} // namespace + +KernelWarningCollector::KernelWarningCollector() { +} + +KernelWarningCollector::~KernelWarningCollector() { +} + +bool KernelWarningCollector::LoadKernelWarning(std::string *content, + std::string *hash_string) { + FilePath kernel_warning_path(kKernelWarningPath); + if (!file_util::ReadFileToString(kernel_warning_path, content)) { + LOG(ERROR) << "Could not open " << kKernelWarningPath; + return false; + } + /* Verify that the first line contains an 8-digit hex hash. */ + *hash_string = content->substr(0, 8); + std::vector output; + if (!base::HexStringToBytes(*hash_string, &output)) { + LOG(ERROR) << "Bad hash " << *hash_string << " in " << kKernelWarningPath; + return false; + } + return true; +} + +bool KernelWarningCollector::Collect() { + std::string reason = "normal collection"; + bool feedback = true; + if (IsDeveloperImage()) { + reason = "always collect from developer builds"; + feedback = true; + } else if (!is_feedback_allowed_function_()) { + reason = "no user consent"; + feedback = false; + } + + LOG(INFO) << "Processing kernel warning: " << reason; + + if (!feedback) { + return true; + } + + std::string kernel_warning; + std::string warning_hash; + if (!LoadKernelWarning(&kernel_warning, &warning_hash)) { + return true; + } + + FilePath root_crash_directory; + if (!GetCreatedCrashDirectoryByEuid(kRootUid, &root_crash_directory, NULL)) { + return true; + } + + std::string dump_basename = + FormatDumpBasename(kExecName, time(NULL), kKernelPid); + FilePath kernel_crash_path = root_crash_directory.Append( + StringPrintf("%s.kcrash", dump_basename.c_str())); + + // We must use WriteNewFile instead of file_util::WriteFile as we + // do not want to write with root access to a symlink that an attacker + // might have created. + if (WriteNewFile(kernel_crash_path, + kernel_warning.data(), + kernel_warning.length()) != + static_cast(kernel_warning.length())) { + LOG(INFO) << "Failed to write kernel warning to " + << kernel_crash_path.value().c_str(); + return true; + } + + AddCrashMetaData(kKernelWarningSignatureKey, warning_hash); + WriteCrashMetaData( + root_crash_directory.Append( + StringPrintf("%s.meta", dump_basename.c_str())), + kExecName, kernel_crash_path.value()); + + LOG(INFO) << "Stored kernel warning into " << kernel_crash_path.value(); + return true; +} diff --git a/crash_reporter/kernel_warning_collector.h b/crash_reporter/kernel_warning_collector.h new file mode 100644 index 000000000..2f8c793b8 --- /dev/null +++ b/crash_reporter/kernel_warning_collector.h @@ -0,0 +1,31 @@ +// Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ +#define _CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ + +#include // for FRIEND_TEST +#include + +#include "crash-reporter/crash_collector.h" + +// Kernel warning collector. +class KernelWarningCollector : public CrashCollector { + public: + KernelWarningCollector(); + + virtual ~KernelWarningCollector(); + + // Collects warning. + bool Collect(); + + private: + friend class KernelWarningCollectorTest; + FRIEND_TEST(KernelWarningCollectorTest, CollectOK); + + // Reads the full content of the kernel warn dump and the warning hash. + bool LoadKernelWarning(std::string *hash, std::string *content); +}; + +#endif // _CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l new file mode 100644 index 000000000..d0977dcf6 --- /dev/null +++ b/crash_reporter/warn_collector.l @@ -0,0 +1,300 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * This flex program reads /var/log/messages as it grows and saves kernel + * warnings to files. It keeps track of warnings it has seen (based on + * file/line only, ignoring differences in the stack trace), and reports only + * the first warning of each kind, but maintains a count of all warnings by + * using their hashes as buckets in a UMA sparse histogram. It also invokes + * the crash collector, which collects the warnings and prepares them for later + * shipment to the crash server. + */ + +%{ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "metrics/c_metrics_library.h" + +int WarnStart(void); +void WarnEnd(void); +void WarnInput(char *buf, int *result, size_t max_size); + +#define YY_INPUT(buf, result, max_size) WarnInput(buf, &result, max_size) + +%} + +/* Define a few useful regular expressions. */ + +D [0-9] +PREFIX .*" kernel: [ "*{D}+"."{D}+"]" +CUT_HERE {PREFIX}" ------------[ cut here".* +WARNING {PREFIX}" WARNING: at " +END_TRACE {PREFIX}" ---[ end trace".* + +/* Use exclusive start conditions. */ +%x PRE_WARN WARN + +%% + /* The scanner itself. */ + +^{CUT_HERE}\n{WARNING} BEGIN(PRE_WARN); +.|\n /* ignore all other input in state 0 */ +.*\n if (WarnStart()) { + BEGIN(WARN); ECHO; + } else { + BEGIN(0); + } + + /* Assume the warning ends at the "end trace" line */ +^{END_TRACE}\n ECHO; BEGIN(0); WarnEnd(); +^.*\n ECHO; + +%% + +#define HASH_BITMAP_SIZE (1 << 15) /* size in bits */ +#define HASH_BITMAP_MASK (HASH_BITMAP_SIZE - 1) + +const char warn_hist_name[] = "Platform.KernelWarningHashes"; +uint32_t hash_bitmap[HASH_BITMAP_SIZE / 32]; +CMetricsLibrary metrics_library; + +const char *prog_name; /* the name of this program */ +int yyin_fd; /* instead of FILE *yyin to avoid buffering */ +int i_fd; /* for inotify, to detect file changes */ +int testing; /* 1 if running test */ +int filter; /* 1 when using as filter (for development) */ +int fifo; /* 1 when reading from fifo (for devel) */ +int draining; /* 1 when draining renamed log file */ + +const char *msg_path = "/var/log/messages"; +const char warn_dump_dir[] = "/var/run/kwarn"; +const char *warn_dump_path = "/var/run/kwarn/warning"; +const char *crash_reporter_command; + +static void Die(const char *format, ...) { + va_list ap; + va_start(ap, format); + fprintf(stderr, "%s: ", prog_name); + vfprintf(stderr, format, ap); + exit(1); +} + +static void RunCrashReporter(void) { + int status = system(crash_reporter_command); + if (status != 0) + Die("%s exited with status %d\n", crash_reporter_command, status); +} + +static uint32_t StringHash(const char *string) { + uint32_t hash = 0; + while (*string != '\0') { + hash = (hash << 5) + hash + *string++; + } + return hash; +} + +/* We expect only a handful of different warnings per boot session, so the + * probability of a collision is very low, and statistically it won't matter + * (unless warnings with the same hash also happens in tandem, which is even + * rarer). + */ +static int HashSeen(uint32_t hash) { + int word_index = (hash & HASH_BITMAP_MASK) / 32; + int bit_index = (hash & HASH_BITMAP_MASK) % 32; + return hash_bitmap[word_index] & 1 << bit_index; +} + +static void SetHashSeen(uint32_t hash) { + int word_index = (hash & HASH_BITMAP_MASK) / 32; + int bit_index = (hash & HASH_BITMAP_MASK) % 32; + hash_bitmap[word_index] |= 1 << bit_index; +} + +int WarnStart(void) { + uint32_t hash; + + if (filter) + return 1; + + hash = StringHash(yytext); + if (!(testing || fifo || filter)) { + CMetricsLibrarySendSparseToUMA(metrics_library, warn_hist_name, (int) hash); + } + if (HashSeen(hash)) + return 0; + SetHashSeen(hash); + + yyout = fopen(warn_dump_path, "w"); + if (yyout == NULL) + Die("fopen %s failed: %s\n", warn_dump_path, strerror(errno)); + fprintf(yyout, "%08x\n", hash); + return 1; +} + +void WarnEnd(void) { + if (filter) + return; + fclose(yyout); + yyout = stdout; /* for debugging */ + RunCrashReporter(); +} + +static void WarnOpenInput(const char *path) { + yyin_fd = open(path, O_RDONLY); + if (yyin_fd < 0) + Die("could not open %s: %s\n", path, strerror(errno)); + if (!fifo) { + /* Set up notification of file growth and rename. */ + i_fd = inotify_init(); + if (i_fd < 0) + Die("inotify_init: %s\n", strerror(errno)); + if (inotify_add_watch(i_fd, path, IN_MODIFY | IN_MOVE_SELF) < 0) + Die("inotify_add_watch: %s\n", strerror(errno)); + } +} + +/* We replace the default YY_INPUT() for the following reasons: + * + * 1. We want to read data as soon as it becomes available, but the default + * YY_INPUT() uses buffered I/O. + * + * 2. We want to block on end of input and wait for the file to grow. + * + * 3. We want to detect log rotation, and reopen the input file as needed. + */ +void WarnInput(char *buf, int *result, size_t max_size) { + while (1) { + *result = read(yyin_fd, buf, max_size); + if (*result < 0) + Die("read: %s", strerror(errno)); + if (*result > 0 || fifo || filter) + return; + if (draining) { + /* Assume we're done with this log, and move to next + * log. Rsyslogd may keep writing to the old log file + * for a while, but we don't care since we don't have + * to be exact. + */ + close(yyin_fd); + if (YYSTATE == WARN) { + /* Be conservative in case we lose the warn + * terminator during the switch---or we may + * collect personally identifiable information. + */ + WarnEnd(); + } + BEGIN(0); /* see above comment */ + sleep(1); /* avoid race with log rotator */ + WarnOpenInput(msg_path); + draining = 0; + continue; + } + /* Nothing left to read, so we must wait. */ + struct inotify_event event; + int n = read(i_fd, &event, sizeof(event)); + if (n <= 0) + Die("inotify: %s\n", strerror(errno)); + if (event.mask & IN_MOVE_SELF) { + /* The file has been renamed. Before switching + * to the new one, we process any remaining + * content of this file. + */ + draining = 1; + } + } +} + +int main(int argc, char **argv) { + int result; + struct passwd *user; + prog_name = argv[0]; + + if (argc == 2 && strcmp(argv[1], "--test") == 0) + testing = 1; + else if (argc == 2 && strcmp(argv[1], "--filter") == 0) + filter = 1; + else if (argc == 2 && strcmp(argv[1], "--fifo") == 0) { + fifo = 1; + } else if (argc != 1) { + fprintf(stderr, + "usage: %s [single-flag]\n" + "flags (for testing only):\n" + "--fifo\tinput is fifo \"fifo\", output is stdout\n" + "--filter\tinput is stdin, output is stdout\n" + "--test\trun self-test\n", + prog_name); + exit(1); + } + + metrics_library = CMetricsLibraryNew(); + CMetricsLibraryInit(metrics_library); + + crash_reporter_command = testing ? + "./warn_collector_test_reporter.sh" : + "/sbin/crash_reporter --kernel_warning"; + + /* When filtering with --filter (for development) use stdin for input. + * Otherwise read input from a file or a fifo. + */ + yyin_fd = fileno(stdin); + if (testing) { + msg_path = "messages"; + warn_dump_path = "warning"; + } + if (fifo) { + msg_path = "fifo"; + } + if (!filter) { + WarnOpenInput(msg_path); + } + + /* Create directory for dump file. Still need to be root here. */ + unlink(warn_dump_path); + if (!testing && !fifo && !filter) { + rmdir(warn_dump_dir); + result = mkdir(warn_dump_dir, 0755); + if (result < 0) + Die("could not create %s: %s\n", + warn_dump_dir, strerror(errno)); + } + + if (0) { + /* TODO(semenzato): put this back in once we decide it's safe + * to make /var/spool/crash rwxrwxrwx root, or use a different + * owner and setuid for the crash reporter as well. + */ + + /* Get low privilege uid, gid. */ + user = getpwnam("chronos"); + if (user == NULL) + Die("getpwnam failed\n"); + + /* Change dump directory ownership. */ + if (chown(warn_dump_dir, user->pw_uid, user->pw_gid) < 0) + Die("chown: %s\n", strerror(errno)); + + /* Drop privileges. */ + if (setuid(user->pw_uid) < 0) { + Die("setuid: %s\n", strerror(errno)); + } + } + + /* Go! */ + return yylex(); +} + +/* Flex should really know not to generate these functions. + */ +void UnusedFunctionWarningSuppressor(void) { + yyunput(0, 0); + (void) input(); +} diff --git a/crash_reporter/warn_collector_test.c b/crash_reporter/warn_collector_test.c new file mode 100644 index 000000000..67f34554e --- /dev/null +++ b/crash_reporter/warn_collector_test.c @@ -0,0 +1,14 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* + * Test driver for the warn_collector daemon. + */ +#include + +int main(int ac, char **av) +{ + return system("$SRC/warn_collector_test.sh"); +} diff --git a/crash_reporter/warn_collector_test.sh b/crash_reporter/warn_collector_test.sh new file mode 100755 index 000000000..e1f8a2909 --- /dev/null +++ b/crash_reporter/warn_collector_test.sh @@ -0,0 +1,73 @@ +#! /bin/bash +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Test for warn_collector. Run the warn collector in the background, emulate +# the kernel by appending lines to the log file "messages", and observe the log +# of the (fake) crash reporter each time is run by the warn collector daemon. + +set -e + +SYSROOT=/build/link +LD_LIBRARY_PATH=$SYSROOT/lib64:$SYSROOT/usr/lib64 +PATH=.:$SYSROOT/bin:$SYSROOT/usr/bin:$PATH +TESTLOG=warn-test-log + +trap cleanup EXIT + +cleanup() { + # Kill daemon (if started) on exit + kill % +} + +check_log() { + n_expected=$1 + if [ ! -f $TESTLOG ]; then + echo $TESTLOG was not created + exit 1 + fi + if [ "$(wc -l < $TESTLOG)" -ne $n_expected ]; then + echo expected $n_expected lines in $TESTLOG, found this: + cat $TESTLOG + exit 1 + fi + if egrep -qv '^[0-9a-f]{8}$' $TESTLOG; then + echo found bad lines in $TESTLOG: + cat $TESTLOG + exit 1 + fi +} + +rm -f warn-test-log +cp $SRC/warn_collector_test_reporter.sh . +cp $SRC/TEST_WARNING . +cp TEST_WARNING messages + +# Start the collector daemon. With the --test option, the daemon reads input +# from ./messages, writes the warning into ./warning, and invokes +# ./warn_collector_test_reporter.sh to report the warning. +warn_collector --test & + +# After a while, check that the first warning has been collected. +sleep 1 +check_log 1 + +# Add the same warning to messages, verify that it is NOT collected +cat TEST_WARNING >> messages +sleep 1 +check_log 1 + +# Add a slightly different warning to messages, check that it is collected. +sed s/intel_dp.c/intel_xx.c/ < TEST_WARNING >> messages +sleep 1 +check_log 2 + +# Emulate log rotation, add a warning, and check. +mv messages messages.1 +sed s/intel_dp.c/intel_xy.c/ < TEST_WARNING > messages +sleep 2 +check_log 3 + +# Success! +exit 0 diff --git a/crash_reporter/warn_collector_test_reporter.sh b/crash_reporter/warn_collector_test_reporter.sh new file mode 100755 index 000000000..d8f3fad94 --- /dev/null +++ b/crash_reporter/warn_collector_test_reporter.sh @@ -0,0 +1,16 @@ +#! /bin/sh +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Replacement for the crash reporter, for testing. Log the first line of the +# "warning" file, which by convention contains the warning hash, and remove the +# file. + +set -e + +exec 1>> warn-test-log +exec 2>> warn-test-log + +head -1 warning +rm warning From 5eb471c41636ad06b65fd692ee01d978236f4441 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Mon, 29 Apr 2013 12:38:36 -0700 Subject: [PATCH 115/225] Hash only warning "file:line" in warning collector. Before this change, the signature of a warning is computed from a string such as this one: /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9() This change removes all text after the first space, i.e. the function name and IP offset. The latter varies across architectures and can vary across builds, giving different signatures for the same warning. BUG=chromium:227080 TEST=manual Change-Id: Ied65dbd8edd734bd152a9dd35aaa48f1e8f1b9e8 Reviewed-on: https://gerrit.chromium.org/gerrit/49505 Commit-Queue: Luigi Semenzato Reviewed-by: Luigi Semenzato Tested-by: Luigi Semenzato --- crash_reporter/warn_collector.l | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l index d0977dcf6..7b6575bf9 100644 --- a/crash_reporter/warn_collector.l +++ b/crash_reporter/warn_collector.l @@ -48,7 +48,7 @@ END_TRACE {PREFIX}" ---[ end trace".* ^{CUT_HERE}\n{WARNING} BEGIN(PRE_WARN); .|\n /* ignore all other input in state 0 */ -.*\n if (WarnStart()) { +[^ ]+\n if (WarnStart()) { /* yytext is file:line */ BEGIN(WARN); ECHO; } else { BEGIN(0); From 426fcc0bd579a87102ea50fb570dbf9834fa2962 Mon Sep 17 00:00:00 2001 From: Albert Chaulk Date: Thu, 2 May 2013 15:38:31 -0700 Subject: [PATCH 116/225] Handle Chrome crashes Chrome will be modified to pass crashes to crash_reporter via a specially formatted file, a TLV-style designed to be easily parsed, instead of multipart/form-data. Extra attributes are added into the .meta file. BUG=chromium:187808 TEST=new unit tests created Change-Id: I0d65d118e0e348329d14bb4004c8f7bad5a44a97 Reviewed-on: https://gerrit.chromium.org/gerrit/49972 Reviewed-by: Chris Masone Commit-Queue: Albert Chaulk Tested-by: Albert Chaulk --- crash_reporter/Makefile | 2 + crash_reporter/chrome_collector.cc | 175 ++++++++++++++++++++++++ crash_reporter/chrome_collector.h | 43 ++++++ crash_reporter/chrome_collector_test.cc | 126 +++++++++++++++++ crash_reporter/crash_collector.cc | 60 ++++++++ crash_reporter/crash_collector.h | 6 + crash_reporter/crash_reporter.cc | 43 +++++- crash_reporter/user_collector.cc | 60 -------- crash_reporter/user_collector.h | 5 - 9 files changed, 449 insertions(+), 71 deletions(-) create mode 100644 crash_reporter/chrome_collector.cc create mode 100644 crash_reporter/chrome_collector.h create mode 100644 crash_reporter/chrome_collector_test.cc diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index 4a0f488f6..c784e6ea5 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -10,6 +10,7 @@ BASE_VER ?= 125070 PKG_CONFIG ?= pkg-config CRASH_OBJS = \ + chrome_collector.o \ crash_collector.o \ kernel_collector.o \ kernel_warning_collector.o \ @@ -60,6 +61,7 @@ $(eval $(call add_object_rules,lex.yy.o,CC,c)) # -- Unit Tests -- # Uncomment these to just run specific test(s). +#TEST_BINS += chrome_collector_test #TEST_BINS += crash_collector_test #TEST_BINS += kernel_collector_test #TEST_BINS += udev_collector_test diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc new file mode 100644 index 000000000..20170bd31 --- /dev/null +++ b/crash_reporter/chrome_collector.cc @@ -0,0 +1,175 @@ +// Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crash-reporter/chrome_collector.h" + +#include +#include +#include + +#include +#include +#include +#include +#include "chromeos/process.h" +#include "chromeos/syslog_logging.h" + +const char kDefaultMinidumpName[] = "upload_file_minidump"; + +namespace { + +// Extract a string delimited by the given character, from the given offset +// into a source string. Returns false if the string is zero-sized or no +// delimiter was found. +bool GetDelimitedString(const std::string &str, char ch, size_t offset, + std::string *substr) { + size_t at = str.find_first_of(ch, offset); + if (at == std::string::npos || at == offset) + return false; + *substr = str.substr(offset, at - offset); + return true; +} + +} //namespace + + +ChromeCollector::ChromeCollector() {} + +ChromeCollector::~ChromeCollector() {} + +bool ChromeCollector::HandleCrash(const std::string &file_path, + const std::string &pid_string, + const std::string &uid_string) { + if (!is_feedback_allowed_function_()) + return true; + + + FilePath dir; + uid_t uid = atoi(uid_string.c_str()); + pid_t pid = atoi(pid_string.c_str()); + if (!GetCreatedCrashDirectoryByEuid(uid, &dir, NULL)) { + LOG(ERROR) << "Can't create crash directory for uid " << uid; + return false; + } + + std::string exec; + if (!GetExecutableBaseNameFromPid(pid, &exec)) { + LOG(ERROR) << "Can't get executable name for pid " << pid; + return false; + } + + std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); + FilePath meta_path = GetCrashPath(dir, dump_basename, "meta"); + FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp"); + FilePath log_path = GetCrashPath(dir, dump_basename, "log"); + + std::string data; + if (!file_util::ReadFileToString(FilePath(file_path), &data)) { + LOG(ERROR) << "Can't read crash log: " << file_path.c_str(); + return false; + } + + if (!ParseCrashLog(data, dir, minidump_path)) { + LOG(ERROR) << "Failed to parse Chrome's crash log"; + return false; + } + + if (GetLogContents(FilePath(log_config_path_), exec, log_path)) + AddCrashMetaData("log", log_path.value()); + + // We're done. + WriteCrashMetaData(meta_path, exec, minidump_path.value()); + + return true; +} + +bool ChromeCollector::ParseCrashLog(const std::string &data, + const FilePath &dir, + const FilePath &minidump) { + size_t at = 0; + while (at < data.size()) { + // Look for a : followed by a decimal number, followed by another : + // followed by N bytes of data. + std::string name, size_string; + if (!GetDelimitedString(data, ':', at, &name)) + break; + at += name.size() + 1; // Skip the name & : delimiter. + + if (!GetDelimitedString(data, ':', at, &size_string)) + break; + at += size_string.size() + 1; // Skip the size & : delimiter. + + size_t size; + if (!base::StringToSizeT(size_string, &size)) + break; + + // Data would run past the end, did we get a truncated file? + if (at + size > data.size()) + break; + + if (name.find("filename") != std::string::npos) { + // File. + // Name will be in a semi-MIME format of + // "; filename="" + // Descriptive name will be upload_file_minidump for the dump. + std::string desc, filename; + pcrecpp::RE re("(.*)\" *; *filename=\"(.*)\""); + if (!re.FullMatch(name.c_str(), &desc, &filename)) + break; + + if (filename.compare(kDefaultMinidumpName) == 0) { + // The minidump. + WriteNewFile(minidump, data.c_str() + at, size); + } else { + // Some other file. + FilePath path = GetCrashPath(dir, filename, "other"); + if (WriteNewFile(path, data.c_str() + at, size) >= 0) { + std::string value = "@"; + value.append(path.value()); + AddCrashMetaData(desc, value); + } + } + } else { + // Other attribute. + std::string value_str; + value_str.reserve(size); + + // Since metadata is one line/value the values must be escaped properly. + for (size_t i = at; i < at + size; i++) { + switch (data[i]) { + case '"': + case '\\': + value_str.push_back('\\'); + value_str.push_back(data[i]); + break; + + case '\r': + value_str += "\\r"; + break; + + case '\n': + value_str += "\\n"; + break; + + case '\t': + value_str += "\\t"; + break; + + case '\0': + value_str += "\\0"; + break; + + default: + value_str.push_back(data[i]); + break; + } + } + AddCrashMetaData(name, value_str); + } + + at += size; + } + + return at == data.size(); +} diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h new file mode 100644 index 000000000..223bb007f --- /dev/null +++ b/crash_reporter/chrome_collector.h @@ -0,0 +1,43 @@ +// Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CRASH_REPORTER_CHROME_COLLECTOR_H_ +#define _CRASH_REPORTER_CHROME_COLLECTOR_H_ + +#include + +#include "base/file_path.h" +#include "crash-reporter/crash_collector.h" +#include "gtest/gtest_prod.h" // for FRIEND_TEST + +class SystemLogging; + +// Chrome crash collector. +class ChromeCollector : public CrashCollector { + public: + ChromeCollector(); + virtual ~ChromeCollector(); + + // Handle a specific chrome crash. Returns true on success. + bool HandleCrash(const std::string &file_path, const std::string &pid_string, + const std::string &uid_string); + + private: + friend class ChromeCollectorTest; + FRIEND_TEST(ChromeCollectorTest, GoodValues); + FRIEND_TEST(ChromeCollectorTest, BadValues); + FRIEND_TEST(ChromeCollectorTest, Newlines); + FRIEND_TEST(ChromeCollectorTest, File); + + // Crashes are expected to be in a TLV-style format of: + // :: + // Length is encoded as a decimal number. It can be zero, but must consist of + // at least one character + // For file values, name actually contains both a description and a filename, + // in a fixed format of: "; filename="" + bool ParseCrashLog(const std::string &data, const base::FilePath &dir, + const base::FilePath &minidump); +}; + +#endif diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc new file mode 100644 index 000000000..b24a194cc --- /dev/null +++ b/crash_reporter/chrome_collector_test.cc @@ -0,0 +1,126 @@ +// Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include + +#include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/string_split.h" +#include "chromeos/syslog_logging.h" +#include "chromeos/test_helpers.h" +#include "crash-reporter/chrome_collector.h" +#include "gflags/gflags.h" +#include "gtest/gtest.h" + +using base::FilePath; + +static const char kCrashFormatGood[] = "value1:10:abcdefghijvalue2:5:12345"; +static const char kCrashFormatEmbeddedNewline[] = + "value1:10:abcd\r\nghijvalue2:5:12\n34"; +static const char kCrashFormatBad1[] = "value1:10:abcdefghijvalue2:6=12345"; +static const char kCrashFormatBad2[] = "value1:10:abcdefghijvalue2:512345"; +static const char kCrashFormatBad3[] = "value1:10::abcdefghijvalue2:5=12345"; +static const char kCrashFormatBad4[] = "value1:10:abcdefghijvalue2:4=12345"; + +static const char kCrashFormatWithFile[] = + "value1:10:abcdefghijvalue2:5:12345" + "some_file\"; filename=\"foo.txt\":15:12345\n789\n12345" + "value3:2:ok"; + +void CountCrash() { + static int s_crashes = 0; + ++s_crashes; +} + +bool IsMetrics() { + return false; +} + +class ChromeCollectorTest : public ::testing::Test { + void SetUp() { + collector_.Initialize(CountCrash, IsMetrics); + pid_ = getpid(); + chromeos::ClearLog(); + } + + protected: + void ExpectFileEquals(const char *golden, + const char *file_path) { + std::string contents; + EXPECT_TRUE(file_util::ReadFileToString(FilePath(file_path), + &contents)); + EXPECT_EQ(golden, contents); + } + + std::vector SplitLines(const std::string &lines) const { + std::vector result; + base::SplitString(lines, '\n', &result); + return result; + } + + ChromeCollector collector_; + pid_t pid_; +}; + +TEST_F(ChromeCollectorTest, GoodValues) { + FilePath dir("."); + EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatGood, + dir, dir.Append("minidump.dmp"))); + + // Check to see if the values made it in properly. + std::string meta = collector_.extra_metadata_; + EXPECT_TRUE(meta.find("value1=abcdefghij") != std::string::npos); + EXPECT_TRUE(meta.find("value2=12345") != std::string::npos); +} + +TEST_F(ChromeCollectorTest, Newlines) { + FilePath dir("."); + EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatEmbeddedNewline, + dir, dir.Append("minidump.dmp"))); + + // Check to see if the values were escaped. + std::string meta = collector_.extra_metadata_; + EXPECT_TRUE(meta.find("value1=abcd\\r\\nghij") != std::string::npos); + EXPECT_TRUE(meta.find("value2=12\\n34") != std::string::npos); +} + +TEST_F(ChromeCollectorTest, BadValues) { + FilePath dir("."); + const struct { + const char *data; + } list[] = { + {kCrashFormatBad1, }, + {kCrashFormatBad2, }, + {kCrashFormatBad3, }, + {kCrashFormatBad4, }, + }; + + for (size_t i = 0; i < sizeof(list) / sizeof(list[0]); i++) { + chromeos::ClearLog(); + EXPECT_FALSE(collector_.ParseCrashLog(list[i].data, + dir, dir.Append("minidump.dmp"))); + } +} + +TEST_F(ChromeCollectorTest, File) { + FilePath dir("."); + EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatWithFile, + dir, dir.Append("minidump.dmp"))); + + // Check to see if the values are still correct and that the file was + // written with the right data. + std::string meta = collector_.extra_metadata_; + EXPECT_TRUE(meta.find("value1=abcdefghij") != std::string::npos); + EXPECT_TRUE(meta.find("value2=12345") != std::string::npos); + EXPECT_TRUE(meta.find("value3=ok") != std::string::npos); + ExpectFileEquals("12345\n789\n12345", "foo.txt.other"); + file_util::Delete(dir.Append("foo.txt.other"), false); +} + +int main(int argc, char **argv) { + SetUpTests(&argc, argv, false); + return RUN_ALL_TESTS(); +} diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 990ef034f..85c0933fc 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -226,6 +226,66 @@ bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid, return true; } +FilePath CrashCollector::GetProcessPath(pid_t pid) { + return FilePath(StringPrintf("/proc/%d", pid)); +} + +bool CrashCollector::GetSymlinkTarget(const FilePath &symlink, + FilePath *target) { + int max_size = 32; + scoped_array buffer; + while (true) { + buffer.reset(new char[max_size + 1]); + ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size); + if (size < 0) { + int saved_errno = errno; + LOG(ERROR) << "Readlink failed on " << symlink.value() << " with " + << saved_errno; + return false; + } + buffer[size] = 0; + if (size == max_size) { + // Avoid overflow when doubling. + if (max_size * 2 > max_size) { + max_size *= 2; + continue; + } else { + return false; + } + } + break; + } + + *target = FilePath(buffer.get()); + return true; +} + +bool CrashCollector::GetExecutableBaseNameFromPid(pid_t pid, + std::string *base_name) { + FilePath target; + FilePath process_path = GetProcessPath(pid); + FilePath exe_path = process_path.Append("exe"); + if (!GetSymlinkTarget(exe_path, &target)) { + LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value() + << " DirectoryExists: " + << file_util::DirectoryExists(process_path); + // Try to further diagnose exe readlink failure cause. + struct stat buf; + int stat_result = stat(exe_path.value().c_str(), &buf); + int saved_errno = errno; + if (stat_result < 0) { + LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result + << " " << saved_errno; + } else { + LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode=" + << buf.st_mode; + } + return false; + } + *base_name = target.BaseName().value(); + return true; +} + // Return true if the given crash directory has not already reached // maximum capacity. bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) { diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 3f0a27d99..949552a8c 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -102,6 +102,12 @@ class CrashCollector { const std::string &basename, const std::string &extension); + base::FilePath GetProcessPath(pid_t pid); + bool GetSymlinkTarget(const base::FilePath &symlink, + base::FilePath *target); + bool GetExecutableBaseNameFromPid(pid_t pid, + std::string *base_name); + // Check given crash directory still has remaining capacity for another // crash. bool CheckHasCapacity(const base::FilePath &crash_directory); diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index d298108e5..914f711a5 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -7,13 +7,14 @@ #include #include -#include "base/file_util.h" -#include "base/command_line.h" -#include "base/logging.h" -#include "base/string_split.h" -#include "base/string_util.h" -#include "base/stringprintf.h" +#include +#include +#include +#include +#include +#include #include "chromeos/syslog_logging.h" +#include "crash-reporter/chrome_collector.h" #include "crash-reporter/kernel_collector.h" #include "crash-reporter/kernel_warning_collector.h" #include "crash-reporter/udev_collector.h" @@ -32,6 +33,9 @@ DEFINE_string(user, "", "User crash info (pid:signal:exec_name)"); DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); DEFINE_string(udev, "", "Udev event description (type:device:subsystem)"); DEFINE_bool(kernel_warning, false, "Report collected kernel warning"); +DEFINE_string(chrome, "", "Chrome crash dump file"); +DEFINE_string(pid, "", "PID of crashing process"); +DEFINE_string(uid, "", "UID of crashing process"); #pragma GCC diagnostic error "-Wstrict-aliasing" static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; @@ -109,6 +113,13 @@ static void CountUserCrash() { LOG_IF(WARNING, status != 0) << "dbus-send running failed"; } +static void CountChromeCrash() { + // For now, consider chrome crashes the same as user crashes for reporting + // purposes. + CountUserCrash(); +} + + static int Initialize(KernelCollector *kernel_collector, UserCollector *user_collector, UncleanShutdownCollector *unclean_shutdown_collector) { @@ -166,6 +177,20 @@ static int HandleUserCrash(UserCollector *user_collector) { return 0; } +static int HandleChromeCrash(ChromeCollector *chrome_collector) { + CHECK(!FLAGS_chrome.empty()) << "--chrome= must be set"; + CHECK(!FLAGS_pid.empty()) << "--pid= must be set"; + CHECK(!FLAGS_uid.empty()) << "--uid= must be set"; + + chromeos::LogToString(true); + bool handled = chrome_collector->HandleCrash(FLAGS_chrome, FLAGS_pid, + FLAGS_uid); + chromeos::LogToString(false); + if (!handled) + return 1; + return 0; +} + static int HandleUdevCrash(UdevCollector *udev_collector) { // Handle a crash indicated by a udev event. CHECK(!FLAGS_udev.empty()) << "--udev= must be set"; @@ -251,6 +276,8 @@ int main(int argc, char *argv[]) { IsFeedbackAllowed); UdevCollector udev_collector; udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed); + ChromeCollector chrome_collector; + chrome_collector.Initialize(CountChromeCrash, IsFeedbackAllowed); KernelWarningCollector kernel_warning_collector; udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed); @@ -279,5 +306,9 @@ int main(int argc, char *argv[]) { return HandleKernelWarning(&kernel_warning_collector); } + if (!FLAGS_chrome.empty()) { + return HandleChromeCrash(&chrome_collector); + } + return HandleUserCrash(&user_collector); } diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index a89ea8169..51be28398 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -134,66 +134,6 @@ bool UserCollector::SetUpInternal(bool enabled) { return true; } -FilePath UserCollector::GetProcessPath(pid_t pid) { - return FilePath(StringPrintf("/proc/%d", pid)); -} - -bool UserCollector::GetSymlinkTarget(const FilePath &symlink, - FilePath *target) { - int max_size = 32; - scoped_array buffer; - while (true) { - buffer.reset(new char[max_size + 1]); - ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size); - if (size < 0) { - int saved_errno = errno; - LOG(ERROR) << "Readlink failed on " << symlink.value() << " with " - << saved_errno; - return false; - } - buffer[size] = 0; - if (size == max_size) { - // Avoid overflow when doubling. - if (max_size * 2 > max_size) { - max_size *= 2; - continue; - } else { - return false; - } - } - break; - } - - *target = FilePath(buffer.get()); - return true; -} - -bool UserCollector::GetExecutableBaseNameFromPid(pid_t pid, - std::string *base_name) { - FilePath target; - FilePath process_path = GetProcessPath(pid); - FilePath exe_path = process_path.Append("exe"); - if (!GetSymlinkTarget(exe_path, &target)) { - LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value() - << " DirectoryExists: " - << file_util::DirectoryExists(process_path); - // Try to further diagnose exe readlink failure cause. - struct stat buf; - int stat_result = stat(exe_path.value().c_str(), &buf); - int saved_errno = errno; - if (stat_result < 0) { - LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result - << " " << saved_errno; - } else { - LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode=" - << buf.st_mode; - } - return false; - } - *base_name = target.BaseName().value(); - return true; -} - bool UserCollector::GetFirstLineWithPrefix( const std::vector &lines, const char *prefix, std::string *line) { diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index c7ac0de50..237a99003 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -101,11 +101,6 @@ class UserCollector : public CrashCollector { std::string GetPattern(bool enabled) const; bool SetUpInternal(bool enabled); - base::FilePath GetProcessPath(pid_t pid); - bool GetSymlinkTarget(const base::FilePath &symlink, - base::FilePath *target); - bool GetExecutableBaseNameFromPid(pid_t pid, - std::string *base_name); // Returns, via |line|, the first line in |lines| that starts with |prefix|. // Returns true if a line is found, or false otherwise. bool GetFirstLineWithPrefix(const std::vector &lines, From 77bd156185efa5f836ca67d09c6e512267fbc6ed Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 22 May 2013 17:47:49 -0400 Subject: [PATCH 117/225] warn_collector: handle non-fatal signals If we pause the process (standard SIGSTOP/SIGCONT), the read() on the inotify handle gets angry and aborts. This can happen if you ctrl+z the program, or if you attach to it with strace. This is undesirable, so ignore EINTR errors from the read(). BUG=None TEST=ran `./warn_collector --test` and hit ctrl+z then fg Change-Id: I49e99abb12ace847e88f973817854d8ba95cf689 Reviewed-on: https://gerrit.chromium.org/gerrit/56303 Reviewed-by: Luigi Semenzato Tested-by: Mike Frysinger Commit-Queue: Mike Frysinger --- crash_reporter/warn_collector.l | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l index 7b6575bf9..2f7f8ac4c 100644 --- a/crash_reporter/warn_collector.l +++ b/crash_reporter/warn_collector.l @@ -200,9 +200,16 @@ void WarnInput(char *buf, int *result, size_t max_size) { } /* Nothing left to read, so we must wait. */ struct inotify_event event; - int n = read(i_fd, &event, sizeof(event)); - if (n <= 0) - Die("inotify: %s\n", strerror(errno)); + while (1) { + int n = read(i_fd, &event, sizeof(event)); + if (n <= 0) { + if (errno == EINTR) + continue; + else + Die("inotify: %s\n", strerror(errno)); + } else + break; + } if (event.mask & IN_MOVE_SELF) { /* The file has been renamed. Before switching * to the new one, we process any remaining From 44054a00520034dca6e29646522314d124021ad5 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 22 May 2013 17:50:39 -0400 Subject: [PATCH 118/225] warn_collector_test: make it actually work The warn_collector_test.c wrapper which calls system() assumes that the return value of system() is the exit code when it's really a wait status. That means we'd often get back values like 256 which, when masked with 0377, we get back 0 and so this test would always "pass". Once we fix that, we see that the script has been failing for all boards other than link because of hardcoded paths in the setup: warn_collector: error while loading shared libraries: libmetrics.so: cannot open shared object file: No such file or directory Analysis of these vars show that we haven't even been testing the local compiled binary. Instead, we've been trying to test the one installed into the $SYSROOT due to the $PATH munging. I cleaned that up as well as generally tidied up this test script. Once we fix that, we then see that the collection doesn't work properly and fails with: expected 2 lines in warn-test-log, found this: 6c1b181c This last bug is due to the regex matching less than was expected: [^ ]+\n if (WarnStart()) { /* yytext is file:line */ But the actual line looks like: Apr 31 25:25:25 localhost kernel: [117959.231100] WARNING: at /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9() You can see there is a space between the and the which means we pass in to the start function in this (*not* the file:line): intel_dp_check_edp+0x6b/0xb9() When the test script runs, it tries to use sed to mung the error message to get a new file path, but the collector only looks at the func name, so the calculated hashes continue to match. We can fix this latter part in two ways: change the test to mung the func name, or change the lex to pass in the file+func name. I've gone the latter route as that seems to have been the intention. BUG=chromium:217382 TEST=`cros_run_unit_tests --board=x86-alex -p crash-reporter` passes Change-Id: I5b3490c56280bb5260198af930b6744cea585c46 Reviewed-on: https://gerrit.chromium.org/gerrit/56305 Reviewed-by: Luigi Semenzato Tested-by: Mike Frysinger Commit-Queue: Mike Frysinger --- crash_reporter/warn_collector.l | 2 +- crash_reporter/warn_collector_test.c | 3 +- crash_reporter/warn_collector_test.sh | 46 +++++++++++++++------------ 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l index 2f7f8ac4c..ee3657e89 100644 --- a/crash_reporter/warn_collector.l +++ b/crash_reporter/warn_collector.l @@ -48,7 +48,7 @@ END_TRACE {PREFIX}" ---[ end trace".* ^{CUT_HERE}\n{WARNING} BEGIN(PRE_WARN); .|\n /* ignore all other input in state 0 */ -[^ ]+\n if (WarnStart()) { /* yytext is file:line */ +[^ ]+.[^ ]+\n if (WarnStart()) { /* yytext is file:line func+offset/offset() */ BEGIN(WARN); ECHO; } else { BEGIN(0); diff --git a/crash_reporter/warn_collector_test.c b/crash_reporter/warn_collector_test.c index 67f34554e..76707b200 100644 --- a/crash_reporter/warn_collector_test.c +++ b/crash_reporter/warn_collector_test.c @@ -10,5 +10,6 @@ int main(int ac, char **av) { - return system("$SRC/warn_collector_test.sh"); + int status = system("exec \"${SRC}\"/warn_collector_test.sh"); + return status < 0 ? EXIT_FAILURE : WEXITSTATUS(status); } diff --git a/crash_reporter/warn_collector_test.sh b/crash_reporter/warn_collector_test.sh index e1f8a2909..0790181ba 100755 --- a/crash_reporter/warn_collector_test.sh +++ b/crash_reporter/warn_collector_test.sh @@ -9,12 +9,20 @@ set -e -SYSROOT=/build/link -LD_LIBRARY_PATH=$SYSROOT/lib64:$SYSROOT/usr/lib64 -PATH=.:$SYSROOT/bin:$SYSROOT/usr/bin:$PATH -TESTLOG=warn-test-log +fail() { + printf '[ FAIL ] %b\n' "$*" + exit 1 +} -trap cleanup EXIT +if [[ -z ${SYSROOT} ]]; then + fail "SYSROOT must be set for this test to work" +fi +: ${OUT:=${PWD}} +cd "${OUT}" +PATH=${OUT}:${PATH} +TESTLOG="${OUT}/warn-test-log" + +echo "Testing: $(which warn_collector)" cleanup() { # Kill daemon (if started) on exit @@ -22,32 +30,30 @@ cleanup() { } check_log() { - n_expected=$1 - if [ ! -f $TESTLOG ]; then - echo $TESTLOG was not created - exit 1 + local n_expected=$1 + if [[ ! -f ${TESTLOG} ]]; then + fail "${TESTLOG} was not created" fi - if [ "$(wc -l < $TESTLOG)" -ne $n_expected ]; then - echo expected $n_expected lines in $TESTLOG, found this: - cat $TESTLOG - exit 1 + if [[ $(wc -l < "${TESTLOG}") -ne ${n_expected} ]]; then + fail "expected ${n_expected} lines in ${TESTLOG}, found this instead: +$(<"${TESTLOG}")" fi - if egrep -qv '^[0-9a-f]{8}$' $TESTLOG; then - echo found bad lines in $TESTLOG: - cat $TESTLOG - exit 1 + if egrep -qv '^[0-9a-f]{8}$' "${TESTLOG}"; then + fail "found bad lines in ${TESTLOG}: +$(<"${TESTLOG}")" fi } -rm -f warn-test-log -cp $SRC/warn_collector_test_reporter.sh . -cp $SRC/TEST_WARNING . +rm -f "${TESTLOG}" +cp "${SRC}/warn_collector_test_reporter.sh" . +cp "${SRC}/TEST_WARNING" . cp TEST_WARNING messages # Start the collector daemon. With the --test option, the daemon reads input # from ./messages, writes the warning into ./warning, and invokes # ./warn_collector_test_reporter.sh to report the warning. warn_collector --test & +trap cleanup EXIT # After a while, check that the first warning has been collected. sleep 1 From b3fe6c3a4e91e4843ff9c03098e3931bec23d447 Mon Sep 17 00:00:00 2001 From: Chris Masone Date: Fri, 31 May 2013 09:37:33 -0700 Subject: [PATCH 119/225] Replace some LOGs with PLOGs PLOG automatically appends an error message appropriate for errno, so use it when logging errors after operations that would set errno meaningfully. BUG=None TEST=None Change-Id: I63549e149bd699469293e5a7f0e48efa9e388162 Reviewed-on: https://gerrit.chromium.org/gerrit/57213 Reviewed-by: Chris Masone Tested-by: Chris Masone Commit-Queue: Chris Masone --- crash_reporter/user_collector.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 51be28398..eefe5dfca 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -120,7 +120,7 @@ bool UserCollector::SetUpInternal(bool enabled) { kCorePipeLimit, strlen(kCorePipeLimit)) != static_cast(strlen(kCorePipeLimit))) { - LOG(ERROR) << "Unable to write " << core_pipe_limit_file_; + PLOG(ERROR) << "Unable to write " << core_pipe_limit_file_; return false; } std::string pattern = GetPattern(enabled); @@ -128,7 +128,7 @@ bool UserCollector::SetUpInternal(bool enabled) { pattern.c_str(), pattern.length()) != static_cast(pattern.length())) { - LOG(ERROR) << "Unable to write " << core_pattern_file_; + PLOG(ERROR) << "Unable to write " << core_pattern_file_; return false; } return true; @@ -218,7 +218,7 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid, bool UserCollector::CopyOffProcFiles(pid_t pid, const FilePath &container_dir) { if (!file_util::CreateDirectory(container_dir)) { - LOG(ERROR) << "Could not create " << container_dir.value().c_str(); + PLOG(ERROR) << "Could not create " << container_dir.value().c_str(); return false; } FilePath process_path = GetProcessPath(pid); @@ -262,7 +262,7 @@ UserCollector::ErrorType UserCollector::ValidateCoreFile( const FilePath &core_path) const { int fd = HANDLE_EINTR(open(core_path.value().c_str(), O_RDONLY)); if (fd < 0) { - LOG(ERROR) << "Could not open core file " << core_path.value(); + PLOG(ERROR) << "Could not open core file " << core_path.value(); return kErrorInvalidCoreFile; } @@ -348,7 +348,7 @@ bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { return true; } - LOG(ERROR) << "Could not write core file"; + PLOG(ERROR) << "Could not write core file"; // If the file system was full, make sure we remove any remnants. file_util::Delete(core_path, false); return false; From f19b518532cb2ab8fc1ed77381809f24a30ac4ca Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 17 May 2013 19:36:47 -0400 Subject: [PATCH 120/225] add support for multiprofile Update crash_sender to scan /home/chronos/u-*/crash/ paths since it's a shell script and there's no easy way to get the right info via shell commands. We want to scan all paths in case of different ordering. For crash_collector, update it to use SessionManager's dbus call to query the active profiles. We select the first one and use that to process crashes. This should be fine. We also need to handle the edge case where no user is logged in (yet a crash occurs with a program running as chronos uid; e.g. the login). In the past, we just wrote to /home/chronos/user/crash/ even when there wasn't a user home dir mounted there. With this change, we formalize (and document) things a bit more by moving to /home/chronos/crash/. We want this behavior rather than re-using the system path as our tests specifically verify system vs user crashes. BUG=chromium:221778 TEST=`cros_run_unit_tests --board=x86-alex -p crash-reporter` passes TEST=logging_UserCrash autotest passes in a vm CQ-DEPEND=CL:56112 CQ-DEPEND=CL:57067 Change-Id: If9f2ffd0740533537718b2a5e7d215200f5a41d6 Reviewed-on: https://gerrit.chromium.org/gerrit/55600 Reviewed-by: Mike Frysinger Tested-by: Mike Frysinger Commit-Queue: Mike Frysinger --- crash_reporter/Makefile | 21 ++++--- crash_reporter/crash_collector.cc | 86 +++++++++++++++++++++++++- crash_reporter/crash_collector.h | 5 ++ crash_reporter/crash_collector_test.cc | 23 ++++++- crash_reporter/crash_collector_test.h | 20 ++++++ crash_reporter/crash_reporter.cc | 5 ++ crash_reporter/crash_sender | 5 +- 7 files changed, 152 insertions(+), 13 deletions(-) create mode 100644 crash_reporter/crash_collector_test.h diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index c784e6ea5..4f716fa58 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -18,17 +18,23 @@ CRASH_OBJS = \ unclean_shutdown_collector.o \ user_collector.o -PC_DEPS = glib-2.0 libpcrecpp libchrome-$(BASE_VER) libchromeos-$(BASE_VER) +PC_DEPS = glib-2.0 gobject-2.0 libpcrecpp libchrome-$(BASE_VER) \ + libchromeos-$(BASE_VER) PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) PC_LIBS := $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) +DBUS_PC_DEPS = dbus-1 dbus-glib-1 +DBUS_PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(DBUS_PC_DEPS)) +DBUS_PC_LIBS := $(shell $(PKG_CONFIG) --libs $(DBUS_PC_DEPS)) + CPPFLAGS += -I$(SRC)/.. -I$(SYSROOT)/usr/include/google-breakpad $(PC_CFLAGS) LDLIBS = -lgflags $(PC_LIBS) # -- Executable crash_reporter -- -CXX_BINARY(crash_reporter): LDLIBS += -lmetrics +CXX_BINARY(crash_reporter): CPPFLAGS += $(DBUS_PC_CFLAGS) +CXX_BINARY(crash_reporter): LDLIBS += -lmetrics $(DBUS_PC_LIBS) CXX_BINARY(crash_reporter): crash_reporter.o $(CRASH_OBJS) clean: CLEAN(crash_reporter) all: CXX_BINARY(crash_reporter) @@ -36,11 +42,8 @@ all: CXX_BINARY(crash_reporter) # -- Executable list_proxies -- -LIST_PROXIES_PKGS = dbus-1 dbus-glib-1 -LIST_PROXIES_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(LIST_PROXIES_PKGS)) -LIST_PROXIES_LIBS := $(shell $(PKG_CONFIG) --libs $(LIST_PROXIES_PKGS)) -CXX_BINARY(list_proxies): CPPFLAGS += $(LIST_PROXIES_CFLAGS) -CXX_BINARY(list_proxies): LDLIBS += $(LIST_PROXIES_LIBS) +CXX_BINARY(list_proxies): CPPFLAGS += $(DBUS_PC_CFLAGS) +CXX_BINARY(list_proxies): LDLIBS += $(DBUS_PC_LIBS) CXX_BINARY(list_proxies): list_proxies.o clean: CLEAN(list_proxies) all: CXX_BINARY(list_proxies) @@ -60,6 +63,8 @@ $(eval $(call add_object_rules,lex.yy.o,CC,c)) # -- Unit Tests -- +UNITTEST_LIBS := $(shell gmock-config --libs) $(shell gtest-config --libs) + # Uncomment these to just run specific test(s). #TEST_BINS += chrome_collector_test #TEST_BINS += crash_collector_test @@ -74,7 +79,7 @@ $(eval $(call add_object_rules,lex.yy.o,CC,c)) # be for "CXX_BINARY()". For example, you could add options with: # CXX_BINARY(crash_collector_test): CXXFLAGS += -some-other-option define TEST_RULES_template -CXX_BINARY($(1)): LDLIBS += -lgtest -lgmock +CXX_BINARY($(1)): LDLIBS += $(UNITTEST_LIBS) $(DBUS_PC_LIBS) CXX_BINARY($(1)): $(1).o $(CRASH_OBJS) clean: CLEAN($(1)) tests: TEST(CXX_BINARY($(1))) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 85c0933fc..55fdd57f8 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -16,12 +16,18 @@ #include #include +#include +#include + #include "base/file_util.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "base/string_split.h" #include "base/string_util.h" #include "base/stringprintf.h" +#include "chromeos/cryptohome.h" +#include "chromeos/dbus/dbus.h" +#include "chromeos/dbus/service_constants.h" #include "chromeos/process.h" static const char kCollectChromeFile[] = @@ -33,7 +39,17 @@ static const char kLeaveCoreFile[] = "/root/.leave_core"; static const char kLsbRelease[] = "/etc/lsb-release"; static const char kShellPath[] = "/bin/sh"; static const char kSystemCrashPath[] = "/var/spool/crash"; -static const char kUserCrashPath[] = "/home/chronos/user/crash"; +// Normally this path is not used. Unfortunately, there are a few edge cases +// where we need this. Any process that runs as kDefaultUserName that crashes +// is consider a "user crash". That includes the initial Chrome browser that +// runs the login screen. If that blows up, there is no logged in user yet, +// so there is no per-user dir for us to stash things in. Instead we fallback +// to this path as it is at least encrypted on a per-system basis. +// +// This also comes up when running autotests. The GUI is sitting at the login +// screen while tests are sshing in, changing users, and triggering crashes as +// the user (purposefully). +static const char kFallbackUserCrashPath[] = "/home/chronos/crash"; // Directory mode of the user crash spool directory. static const mode_t kUserCrashPathMode = 0755; @@ -123,6 +139,72 @@ FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory, extension.c_str())); } +namespace { + +const char *GetGErrorMessage(const GError *error) { + if (!error) + return "Unknown error."; + return error->message; +} + +} + +GHashTable *CrashCollector::GetActiveUserSessions(void) { + GHashTable *active_sessions = NULL; + + chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); + if (!dbus.HasConnection()) { + LOG(ERROR) << "Error connecting to system D-Bus"; + return active_sessions; + } + chromeos::dbus::Proxy proxy(dbus, + login_manager::kSessionManagerServiceName, + login_manager::kSessionManagerServicePath, + login_manager::kSessionManagerInterface); + if (!proxy) { + LOG(ERROR) << "Error creating D-Bus proxy to interface " + << "'" << login_manager::kSessionManagerServiceName << "'"; + return active_sessions; + } + + // Request all the active sessions. + GError *gerror = NULL; + if (!dbus_g_proxy_call(proxy.gproxy(), + login_manager::kSessionManagerRetrieveActiveSessions, + &gerror, G_TYPE_INVALID, + DBUS_TYPE_G_STRING_STRING_HASHTABLE, &active_sessions, + G_TYPE_INVALID)) { + LOG(ERROR) << "Error performing D-Bus proxy call " + << "'" + << login_manager::kSessionManagerRetrieveActiveSessions << "'" + << ": " << GetGErrorMessage(gerror); + return active_sessions; + } + + return active_sessions; +} + +FilePath CrashCollector::GetUserCrashPath(void) { + // In this multiprofile world, there is no one-specific user dir anymore. + // Ask the session manager for the active ones, then just run with the + // first result we get back. + FilePath user_path = FilePath(kFallbackUserCrashPath); + GHashTable *active_sessions = GetActiveUserSessions(); + if (!active_sessions) + return user_path; + + GList *list = g_hash_table_get_values(active_sessions); + if (list) { + const char *salted_path = static_cast(list->data); + user_path = chromeos::cryptohome::home::GetHashedUserPath(salted_path); + g_list_free(list); + } + + g_hash_table_destroy(active_sessions); + + return user_path; +} + FilePath CrashCollector::GetCrashDirectoryInfo( uid_t process_euid, uid_t default_user_id, @@ -140,7 +222,7 @@ FilePath CrashCollector::GetCrashDirectoryInfo( *mode = kUserCrashPathMode; *directory_owner = default_user_id; *directory_group = default_user_group; - return FilePath(kUserCrashPath); + return GetUserCrashPath(); } else { *mode = kSystemCrashPathMode; *directory_owner = kRootOwner; diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 949552a8c..4cbe1b977 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -10,6 +10,8 @@ #include #include +#include + #include "base/file_path.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST @@ -40,6 +42,7 @@ class CrashCollector { FRIEND_TEST(CrashCollectorTest, FormatDumpBasename); FRIEND_TEST(CrashCollectorTest, Initialize); FRIEND_TEST(CrashCollectorTest, IsCommentLine); + FRIEND_TEST(CrashCollectorTest, IsUserSpecificDirectoryEnabled); FRIEND_TEST(CrashCollectorTest, MetaData); FRIEND_TEST(CrashCollectorTest, ReadKeyValueFile); FRIEND_TEST(CrashCollectorTest, Sanitize); @@ -72,6 +75,8 @@ class CrashCollector { forced_crash_directory_ = forced_directory; } + virtual GHashTable *GetActiveUserSessions(void); + base::FilePath GetUserCrashPath(void); base::FilePath GetCrashDirectoryInfo(uid_t process_euid, uid_t default_user_id, gid_t default_user_group, diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index fff22b53f..b9a48a080 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -2,8 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "crash-reporter/crash_collector_test.h" + #include +#include +#include + #include "base/file_util.h" #include "base/string_util.h" #include "base/stringprintf.h" @@ -21,6 +26,7 @@ static const char kBinFalse[] = "/bin/false"; using base::FilePath; using chromeos::FindLog; +using ::testing::Return; void CountCrash() { ADD_FAILURE(); @@ -48,7 +54,7 @@ class CrashCollectorTest : public ::testing::Test { bool CheckHasCapacity(); protected: - CrashCollector collector_; + CrashCollectorMock collector_; FilePath test_dir_; }; @@ -115,13 +121,25 @@ TEST_F(CrashCollectorTest, GetCrashDirectoryInfo) { EXPECT_EQ(kRootUid, directory_owner); EXPECT_EQ(kRootGid, directory_group); + // No need to destroy the hash as GetCrashDirectoryInfo() will do it for us. + GHashTable *active_sessions = g_hash_table_new (g_str_hash, g_str_equal); + char kUser[] = "chicken@butt.com"; + char kHash[] = "hashcakes"; + g_hash_table_insert (active_sessions, + static_cast(kUser), + static_cast(kHash)); + EXPECT_CALL(collector_, GetActiveUserSessions()) + .WillOnce(Return(active_sessions)); + + EXPECT_EQ(collector_.IsUserSpecificDirectoryEnabled(), true); + path = collector_.GetCrashDirectoryInfo(kChronosUid, kChronosUid, kChronosGid, &directory_mode, &directory_owner, &directory_group); - EXPECT_EQ("/home/chronos/user/crash", path.value()); + EXPECT_EQ("/home/user/hashcakes", path.value()); EXPECT_EQ(kExpectedUserMode, directory_mode); EXPECT_EQ(kChronosUid, directory_owner); EXPECT_EQ(kChronosGid, directory_group); @@ -344,6 +362,7 @@ TEST_F(CrashCollectorTest, GetLogContents) { } int main(int argc, char **argv) { + ::g_type_init(); SetUpTests(&argc, argv, false); return RUN_ALL_TESTS(); } diff --git a/crash_reporter/crash_collector_test.h b/crash_reporter/crash_collector_test.h new file mode 100644 index 000000000..71b42b7dd --- /dev/null +++ b/crash_reporter/crash_collector_test.h @@ -0,0 +1,20 @@ +// Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef _CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_ +#define _CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_ + +#include "crash-reporter/crash_collector.h" + +#include + +#include +#include + +class CrashCollectorMock : public CrashCollector { + public: + MOCK_METHOD0(GetActiveUserSessions, GHashTable *()); +}; + +#endif // _CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_ diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 914f711a5..9bdd564ae 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -7,6 +7,8 @@ #include #include +#include + #include #include #include @@ -263,6 +265,9 @@ int main(int argc, char *argv[]) { CommandLine::Init(argc, argv); chromeos::OpenLog(my_path.BaseName().value().c_str(), true); chromeos::InitLog(chromeos::kLogToSyslog); + + ::g_type_init(); + KernelCollector kernel_collector; kernel_collector.Initialize(CountKernelCrash, IsFeedbackAllowed); diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 97045d408..bb8d04fce 100644 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -492,7 +492,10 @@ main() { send_crashes "/var/spool/crash" # Send user-specific crashes - send_crashes "/home/chronos/user/crash" + local d + for d in /home/chronos/crash /home/chronos/u-*/crash; do + send_crashes "${d}" + done } main From 20980d71ee3c6639f8ba928b0c660da3989dc9aa Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Tue, 28 May 2013 14:29:43 -0700 Subject: [PATCH 121/225] Fix initialization of kernel_crash_reporter objects. This fixes a copy-paste error that initialized (twice) the wrong object. BUG=chromium:244574 TEST=none Change-Id: I8ada187483a7b8dbfd8ec19ca284cf08a685844e Reviewed-on: https://gerrit.chromium.org/gerrit/56845 Reviewed-by: Sonny Rao Tested-by: Luigi Semenzato Commit-Queue: Luigi Semenzato --- crash_reporter/crash_reporter.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 9bdd564ae..0334360ce 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -285,7 +285,7 @@ int main(int argc, char *argv[]) { chrome_collector.Initialize(CountChromeCrash, IsFeedbackAllowed); KernelWarningCollector kernel_warning_collector; - udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed); + kernel_warning_collector.Initialize(CountUdevCrash, IsFeedbackAllowed); if (FLAGS_init) { return Initialize(&kernel_collector, From 37843a9f8a46cd5afa57505bdb6d75aa1a75c9d2 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 11 Jun 2013 17:03:59 -0400 Subject: [PATCH 122/225] crash_collector: add /crash to the user crash path The recent multiprofile work forgot to append /crash to the user-specific crash path. It was in the fallback path, but not the dynamic one. BUG=chromium:248629 TEST=`cros_run_unit_tests --board=x86-alex -p crash-reporter` works TEST=`cbuildbot x86-generic-full` works Change-Id: If87848526b0e78f3f76fd412b0f46e37b7022bd6 Reviewed-on: https://gerrit.chromium.org/gerrit/58232 Reviewed-by: Chris Masone Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_collector.cc | 3 ++- crash_reporter/crash_collector_test.cc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 55fdd57f8..b433bec65 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -196,7 +196,8 @@ FilePath CrashCollector::GetUserCrashPath(void) { GList *list = g_hash_table_get_values(active_sessions); if (list) { const char *salted_path = static_cast(list->data); - user_path = chromeos::cryptohome::home::GetHashedUserPath(salted_path); + user_path = chromeos::cryptohome::home::GetHashedUserPath(salted_path) + .Append("crash"); g_list_free(list); } diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index b9a48a080..64a8653e1 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -139,7 +139,7 @@ TEST_F(CrashCollectorTest, GetCrashDirectoryInfo) { &directory_mode, &directory_owner, &directory_group); - EXPECT_EQ("/home/user/hashcakes", path.value()); + EXPECT_EQ("/home/user/hashcakes/crash", path.value()); EXPECT_EQ(kExpectedUserMode, directory_mode); EXPECT_EQ(kChronosUid, directory_owner); EXPECT_EQ(kChronosGid, directory_group); From fc6bafab0eb85ddc05f937faae33afaea248e35b Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 13 May 2013 20:37:09 -0400 Subject: [PATCH 123/225] crash_sender: disable uploading on test images Not sure what has changed, but we're uploading crashes now for test images when we don't want to. When the channel in /etc/lsb-release is marked as a test channel (which happens for all test images), exit early. BUG=chromium:239862 TEST=passed vmtests (logging_CrashSender & logging_UserCrash) TEST=`cbuildbot {arm,amd64,x86}-generic-full` pass Change-Id: I4a35aaa34b590c575de639888ed8be68bc9608b6 Reviewed-on: https://gerrit.chromium.org/gerrit/51084 Reviewed-by: Chris Masone Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_sender | 39 +++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) mode change 100644 => 100755 crash_reporter/crash_sender diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender old mode 100644 new mode 100755 index bb8d04fce..6bebb6ea8 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -155,6 +155,17 @@ is_developer_image() { return 1 } +# Returns 0 if we should consider ourselves to be running on a test image. +is_test_image() { + # If we're testing crash reporter itself, we don't want to special-case + # for test images. + is_crash_test_in_progress && return 1 + case $(get_channel) in + test*) return 0;; + esac + return 1 +} + # Returns 0 if the machine booted up in developer mode. is_developer_mode() { [ ${MOCK_DEVELOPER_MODE} -ne 0 ] && return 0 @@ -229,16 +240,25 @@ get_kind() { } get_key_value() { - if ! grep -q "$2=" "$1"; then - echo "undefined" - return + local file=$1 key=$2 value + + if [ -f "${file}" ]; then + # Return the first entry. There shouldn't be more than one anyways. + value=$(awk -F= '/^'"${key}"'[[:space:]]*=/ { print $NF }' "${file}") fi - grep "$2=" "$1" | cut -d = -f 2- + + echo "${value:-undefined}" } # Return the board name. get_board() { - echo $(get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_BOARD") + get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_BOARD" +} + +# Return the channel name (sans "-channel" suffix). +get_channel() { + get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_TRACK" | + sed 's:-channel$::' } # Return the hardware class or "undefined". @@ -268,7 +288,9 @@ send_crash() { local send_payload_size="$(stat --printf=%s "${report_payload}" 2>/dev/null)" local image_type - if is_developer_image; then + if is_test_image; then + image_type="test" + elif is_developer_image; then image_type="dev" elif [ ${FORCE_OFFICIAL} -ne 0 ]; then image_type="force-official" @@ -476,6 +498,11 @@ main() { exit 1 fi + if is_test_image; then + lecho "Exiting early due to test image." + exit 1 + fi + check_not_already_running for dependency in "${FIND}" "${METRICS_CLIENT}" \ From 72e72712b901f935dcfc2acd11180ee63af529c2 Mon Sep 17 00:00:00 2001 From: Albert Chaulk Date: Mon, 17 Jun 2013 13:56:48 -0700 Subject: [PATCH 124/225] Update chrome crash reports to require the exe name When invoked from chrome we execute too late to get any of the crashing process' info as it's already been cleaned up by the kernel. So, require chrome to pass along the name too BUG=chromium:216523 TEST=manual invocation of /sbin/crash_reporter, verified supplied name gets used & it doesn't check /proc for any info Change-Id: Id74cae3ba93426bd0ac74959741a11baf83c2694 Reviewed-on: https://gerrit.chromium.org/gerrit/58929 Reviewed-by: Mike Frysinger Commit-Queue: Albert Chaulk Tested-by: Albert Chaulk --- crash_reporter/chrome_collector.cc | 15 +++++---------- crash_reporter/chrome_collector.h | 2 +- crash_reporter/crash_reporter.cc | 4 +++- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 20170bd31..d8250f73f 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -40,7 +40,8 @@ ChromeCollector::~ChromeCollector() {} bool ChromeCollector::HandleCrash(const std::string &file_path, const std::string &pid_string, - const std::string &uid_string) { + const std::string &uid_string, + const std::string &exe_name) { if (!is_feedback_allowed_function_()) return true; @@ -53,13 +54,7 @@ bool ChromeCollector::HandleCrash(const std::string &file_path, return false; } - std::string exec; - if (!GetExecutableBaseNameFromPid(pid, &exec)) { - LOG(ERROR) << "Can't get executable name for pid " << pid; - return false; - } - - std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); + std::string dump_basename = FormatDumpBasename(exe_name, time(NULL), pid); FilePath meta_path = GetCrashPath(dir, dump_basename, "meta"); FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp"); FilePath log_path = GetCrashPath(dir, dump_basename, "log"); @@ -75,11 +70,11 @@ bool ChromeCollector::HandleCrash(const std::string &file_path, return false; } - if (GetLogContents(FilePath(log_config_path_), exec, log_path)) + if (GetLogContents(FilePath(log_config_path_), exe_name, log_path)) AddCrashMetaData("log", log_path.value()); // We're done. - WriteCrashMetaData(meta_path, exec, minidump_path.value()); + WriteCrashMetaData(meta_path, exe_name, minidump_path.value()); return true; } diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h index 223bb007f..e25cd86a6 100644 --- a/crash_reporter/chrome_collector.h +++ b/crash_reporter/chrome_collector.h @@ -21,7 +21,7 @@ class ChromeCollector : public CrashCollector { // Handle a specific chrome crash. Returns true on success. bool HandleCrash(const std::string &file_path, const std::string &pid_string, - const std::string &uid_string); + const std::string &uid_string, const std::string &exe_name); private: friend class ChromeCollectorTest; diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 0334360ce..134e8e4ed 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -38,6 +38,7 @@ DEFINE_bool(kernel_warning, false, "Report collected kernel warning"); DEFINE_string(chrome, "", "Chrome crash dump file"); DEFINE_string(pid, "", "PID of crashing process"); DEFINE_string(uid, "", "UID of crashing process"); +DEFINE_string(exe, "", "Executable name of crashing process"); #pragma GCC diagnostic error "-Wstrict-aliasing" static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; @@ -183,10 +184,11 @@ static int HandleChromeCrash(ChromeCollector *chrome_collector) { CHECK(!FLAGS_chrome.empty()) << "--chrome= must be set"; CHECK(!FLAGS_pid.empty()) << "--pid= must be set"; CHECK(!FLAGS_uid.empty()) << "--uid= must be set"; + CHECK(!FLAGS_exe.empty()) << "--exe= must be set"; chromeos::LogToString(true); bool handled = chrome_collector->HandleCrash(FLAGS_chrome, FLAGS_pid, - FLAGS_uid); + FLAGS_uid, FLAGS_exe); chromeos::LogToString(false); if (!handled) return 1; From 5f23bbb37e95913495d28a9707f198175dea4ac4 Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Tue, 16 Apr 2013 15:00:27 -0400 Subject: [PATCH 125/225] Add support for sending Atmel TP/TS error message This adds the support that when Pixel touchscreen/trackpad driver atmel_mxt_ts is misbehaving, e.g. failed to update firmware, failed to read messages from the chip, trigger crash reporter to send system logging message for further debugging. We also follow the policy that, for atmel/cypress logs, we only fetch the last 30s of the logs. BUG=chromium:226186 TEST=1. On Pixel, replace the touchscreen firmware file /opt/google/touch/firmware/162.0_1.1.170.bin with an empty file with the same name 2. Force touchscreen firmware update /opt/google/touch/scripts/chromeos-touch-firmware-update.sh -d atmel_mxt_ts -f -n maxtouch-ts.fw 3. After the firmware update fails, check crash report exists at /var/spool/crash/change__i2c_atmel_mxt_ts.XXX 4. Check that the log contains only 30s of entries with "atmel" keyword. Change-Id: If46a575491378405e60ad1ccbd39026ae6bf2033 Reviewed-on: https://gerrit.chromium.org/gerrit/48239 Reviewed-by: Mike Frysinger Reviewed-by: Will Drewry Commit-Queue: Yufeng Shen Tested-by: Yufeng Shen --- crash_reporter/99-crash-reporter.rules | 2 ++ crash_reporter/crash_reporter_logs.conf | 5 ++- crash_reporter/kernel_log_collector.sh | 48 +++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 crash_reporter/kernel_log_collector.sh diff --git a/crash_reporter/99-crash-reporter.rules b/crash_reporter/99-crash-reporter.rules index a0658210f..57a71cac5 100644 --- a/crash_reporter/99-crash-reporter.rules +++ b/crash_reporter/99-crash-reporter.rules @@ -1,3 +1,5 @@ ACTION=="change", SUBSYSTEM=="drm", KERNEL=="card0", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=KERNEL=card0:SUBSYSTEM=drm:ACTION=change" # For detecting cypress trackpad issue. Passing into crash_reporter SUBSYSTEM=i2c-cyapa since crash_reporter does not handle DRIVER string. ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="cyapa", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-cyapa:ACTION=change" +# For detecting Atmel trackpad/touchscreen issue. Passing into crash_reporter SUBSYSTEM=i2c-atmel_mxt_ts since crash_reporter does not handle DRIVER string. +ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="atmel_mxt_ts", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-atmel_mxt_ts:ACTION=change" diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 0e233d9c8..808e71d24 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -42,7 +42,10 @@ chrome:for dri in /sys/kernel/debug/dri/*; do echo "===$dri/i915_error_state===" # When trackpad driver cyapa detects some abnormal behavior, we collect # additional logs from kernel messages. -crash_reporter-udev-collection-change--i2c-cyapa:grep cyapa /var/log/messages +crash_reporter-udev-collection-change--i2c-cyapa:/usr/sbin/kernel_log_collector.sh cyapa 30 +# When trackpad/touchscreen driver atmel_mxt_ts detects some abnormal behavior, we collect +# additional logs from kernel messages. +crash_reporter-udev-collection-change--i2c-atmel_mxt_ts:/usr/sbin/kernel_log_collector.sh atmel 30 # When touch device noise are detected, we collect relevant logs. (crosbug.com/p/16788) crash_reporter-udev-collection---TouchNoise:cat /var/log/touch_noise.log # Periodically collect touch event log for debugging (crosbug.com/p/17244) diff --git a/crash_reporter/kernel_log_collector.sh b/crash_reporter/kernel_log_collector.sh new file mode 100644 index 000000000..a9d4307d1 --- /dev/null +++ b/crash_reporter/kernel_log_collector.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Usage example: "kernel_log_collector.sh XXX YYY" +# This script searches logs in the /var/log/messages which have the keyword XXX. +# And only those logs which are within the last YYY seconds of the latest log +# that has the keyword XXX are printed. + +# Kernel log has the format +# 2013-06-14T16:31:40.514513-07:00 localhost kernel: [ 2.682472] MSG MSG ... + +search_key=$1 +time_duration=$2 +msg_pattern="^[0-9-]*T[0-9:.-]* localhost kernel" + +die() { + echo "kernel_log_collector: $*" >&2 + exit 1 +} + +get_timestamp() { + timestamp="$(echo $1 | cut -d " " -f 1)" + timestamp="$(date -d "${timestamp}" +%s)" || exit $? + echo "${timestamp}" +} + +last_line=$(grep "${msg_pattern}" /var/log/messages | grep -- "${search_key}" | tail -n 1) + +if [ -n "${last_line}" ]; then + if ! allowed_timestamp=$(get_timestamp "${last_line}"); then + die "coule not get timestamp from: ${last_line}" + fi + : $(( allowed_timestamp -= ${time_duration} )) + grep "${msg_pattern}" /var/log/messages | grep -- "${search_key}" | while read line; do + if ! timestamp=$(get_timestamp "${line}"); then + die "could not get timestamp from: ${line}" + fi + if [ ${timestamp} -gt ${allowed_timestamp} ]; then + echo "${line}" + fi + done +fi + +echo "END-OF-LOG" + From 4c9f5766056768e76c5c374e7e7878c6b932efd5 Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Wed, 19 Jun 2013 16:50:37 -0400 Subject: [PATCH 126/225] kernel_log_collector.sh : correct kernel message pattern The timestamp in the kernel message can have either "+" or "-" sign. We want to match both of them. BUG=chromium:226186 TEST=Make sure the script can capture message starting with "2013-06-19T20:38:58.663410+00:00 localhost kernel: [" Change-Id: I93647d4fc8b3d796166d2e959b24364de4bc60c5 Reviewed-on: https://gerrit.chromium.org/gerrit/59297 Reviewed-by: Mike Frysinger Commit-Queue: Yufeng Shen Tested-by: Yufeng Shen --- crash_reporter/kernel_log_collector.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crash_reporter/kernel_log_collector.sh b/crash_reporter/kernel_log_collector.sh index a9d4307d1..d38479ea5 100644 --- a/crash_reporter/kernel_log_collector.sh +++ b/crash_reporter/kernel_log_collector.sh @@ -9,12 +9,13 @@ # And only those logs which are within the last YYY seconds of the latest log # that has the keyword XXX are printed. -# Kernel log has the format +# Kernel log has the possible formats: # 2013-06-14T16:31:40.514513-07:00 localhost kernel: [ 2.682472] MSG MSG ... +# 2013-06-19T20:38:58.661826+00:00 localhost kernel: [ 1.668092] MSG MSG ... search_key=$1 time_duration=$2 -msg_pattern="^[0-9-]*T[0-9:.-]* localhost kernel" +msg_pattern="^[0-9-]*T[0-9:.+-]* localhost kernel" die() { echo "kernel_log_collector: $*" >&2 From 32f827f9ddc7fdb1264e7bea0ed52b3fecc2826d Mon Sep 17 00:00:00 2001 From: mukesh agrawal Date: Tue, 2 Jul 2013 13:00:01 -0700 Subject: [PATCH 127/225] crash_collector: pedantically document hashed string for warn_collector When computing a hash of a kernel warning message, we include the trailing newline character. Document this fact, to make it easier to write separate tools that compute the hash. BUG=none TEST=cros_workon_make --board=link --test --reconf crash-reporter Change-Id: I5d78235db5c8bff7e3639c9d1cd3915d343f471e Reviewed-on: https://gerrit.chromium.org/gerrit/60797 Commit-Queue: mukesh agrawal Reviewed-by: mukesh agrawal Tested-by: mukesh agrawal --- crash_reporter/warn_collector.l | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l index ee3657e89..6fc2f5638 100644 --- a/crash_reporter/warn_collector.l +++ b/crash_reporter/warn_collector.l @@ -48,7 +48,9 @@ END_TRACE {PREFIX}" ---[ end trace".* ^{CUT_HERE}\n{WARNING} BEGIN(PRE_WARN); .|\n /* ignore all other input in state 0 */ -[^ ]+.[^ ]+\n if (WarnStart()) { /* yytext is file:line func+offset/offset() */ +[^ ]+.[^ ]+\n if (WarnStart()) { + /* yytext is + "file:line func+offset/offset()\n" */ BEGIN(WARN); ECHO; } else { BEGIN(0); From 33dfd47146c14bbdf57494051fe1c1c50949305f Mon Sep 17 00:00:00 2001 From: Albert Chaulk Date: Wed, 19 Jun 2013 15:34:13 -0700 Subject: [PATCH 128/225] Add error logging and fix uploading for chrome crash reports Adds some logging of parse errors in case there's a format mismatch in future. Fixes detection of the minidump, was checking the wrong variable. Modifies crash_sender to properly upload a Chrome dump, including all of the extra options that get included, and identifies it as a Chrome and not ChromeOS dump so that it shows up in the right lists server-side. BUG=chromium:216523 TEST=loaded build of chrome with matching changes, verified about:crash causes a dump to be created and placed in the right spot, and that it uploads properly by crash_sender Change-Id: I8a114ad6798f09f33b78df1680153c0412eabf45 Reviewed-on: https://gerrit.chromium.org/gerrit/59572 Reviewed-by: Albert Chaulk Tested-by: Albert Chaulk Commit-Queue: Albert Chaulk --- crash_reporter/chrome_collector.cc | 40 +++++++---- crash_reporter/chrome_collector.h | 3 +- crash_reporter/chrome_collector_test.cc | 17 +++-- crash_reporter/crash_collector.cc | 14 ++++ crash_reporter/crash_collector.h | 10 +++ crash_reporter/crash_sender | 96 ++++++++++++++++++------- 6 files changed, 134 insertions(+), 46 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index d8250f73f..2c9ae6e81 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -45,6 +45,10 @@ bool ChromeCollector::HandleCrash(const std::string &file_path, if (!is_feedback_allowed_function_()) return true; + if (exe_name.find('/') != std::string::npos) { + LOG(ERROR) << "exe_name contains illegal characters: " << exe_name; + return false; + } FilePath dir; uid_t uid = atoi(uid_string.c_str()); @@ -65,7 +69,7 @@ bool ChromeCollector::HandleCrash(const std::string &file_path, return false; } - if (!ParseCrashLog(data, dir, minidump_path)) { + if (!ParseCrashLog(data, dir, minidump_path, dump_basename)) { LOG(ERROR) << "Failed to parse Chrome's crash log"; return false; } @@ -81,27 +85,37 @@ bool ChromeCollector::HandleCrash(const std::string &file_path, bool ChromeCollector::ParseCrashLog(const std::string &data, const FilePath &dir, - const FilePath &minidump) { + const FilePath &minidump, + const std::string &basename) { size_t at = 0; while (at < data.size()) { // Look for a : followed by a decimal number, followed by another : // followed by N bytes of data. std::string name, size_string; - if (!GetDelimitedString(data, ':', at, &name)) + if (!GetDelimitedString(data, ':', at, &name)) { + LOG(ERROR) << "Can't find : after name @ offset " << at; break; + } at += name.size() + 1; // Skip the name & : delimiter. - if (!GetDelimitedString(data, ':', at, &size_string)) + if (!GetDelimitedString(data, ':', at, &size_string)) { + LOG(ERROR) << "Can't find : after size @ offset " << at; break; + } at += size_string.size() + 1; // Skip the size & : delimiter. size_t size; - if (!base::StringToSizeT(size_string, &size)) + if (!base::StringToSizeT(size_string, &size)) { + LOG(ERROR) << "String not convertible to integer: " << size_string; break; + } // Data would run past the end, did we get a truncated file? - if (at + size > data.size()) + if (at + size > data.size()) { + LOG(ERROR) << "Overrun, expected " << size << " bytes of data, got " + << (data.size() - at); break; + } if (name.find("filename") != std::string::npos) { // File. @@ -110,19 +124,19 @@ bool ChromeCollector::ParseCrashLog(const std::string &data, // Descriptive name will be upload_file_minidump for the dump. std::string desc, filename; pcrecpp::RE re("(.*)\" *; *filename=\"(.*)\""); - if (!re.FullMatch(name.c_str(), &desc, &filename)) + if (!re.FullMatch(name.c_str(), &desc, &filename)) { + LOG(ERROR) << "Filename was not in expected format: " << name; break; + } - if (filename.compare(kDefaultMinidumpName) == 0) { + if (desc.compare(kDefaultMinidumpName) == 0) { // The minidump. WriteNewFile(minidump, data.c_str() + at, size); } else { // Some other file. - FilePath path = GetCrashPath(dir, filename, "other"); + FilePath path = GetCrashPath(dir, basename + "-" + filename, "other"); if (WriteNewFile(path, data.c_str() + at, size) >= 0) { - std::string value = "@"; - value.append(path.value()); - AddCrashMetaData(desc, value); + AddCrashMetaUploadFile(desc, path.value()); } } } else { @@ -160,7 +174,7 @@ bool ChromeCollector::ParseCrashLog(const std::string &data, break; } } - AddCrashMetaData(name, value_str); + AddCrashMetaUploadData(name, value_str); } at += size; diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h index e25cd86a6..0a8a95671 100644 --- a/crash_reporter/chrome_collector.h +++ b/crash_reporter/chrome_collector.h @@ -37,7 +37,8 @@ class ChromeCollector : public CrashCollector { // For file values, name actually contains both a description and a filename, // in a fixed format of: "; filename="" bool ParseCrashLog(const std::string &data, const base::FilePath &dir, - const base::FilePath &minidump); + const base::FilePath &minidump, + const std::string &basename); }; #endif diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc index b24a194cc..9a5ee9bf5 100644 --- a/crash_reporter/chrome_collector_test.cc +++ b/crash_reporter/chrome_collector_test.cc @@ -68,7 +68,8 @@ class ChromeCollectorTest : public ::testing::Test { TEST_F(ChromeCollectorTest, GoodValues) { FilePath dir("."); EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatGood, - dir, dir.Append("minidump.dmp"))); + dir, dir.Append("minidump.dmp"), + "base")); // Check to see if the values made it in properly. std::string meta = collector_.extra_metadata_; @@ -79,7 +80,8 @@ TEST_F(ChromeCollectorTest, GoodValues) { TEST_F(ChromeCollectorTest, Newlines) { FilePath dir("."); EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatEmbeddedNewline, - dir, dir.Append("minidump.dmp"))); + dir, dir.Append("minidump.dmp"), + "base")); // Check to see if the values were escaped. std::string meta = collector_.extra_metadata_; @@ -101,14 +103,16 @@ TEST_F(ChromeCollectorTest, BadValues) { for (size_t i = 0; i < sizeof(list) / sizeof(list[0]); i++) { chromeos::ClearLog(); EXPECT_FALSE(collector_.ParseCrashLog(list[i].data, - dir, dir.Append("minidump.dmp"))); + dir, dir.Append("minidump.dmp"), + "base")); } } TEST_F(ChromeCollectorTest, File) { FilePath dir("."); EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatWithFile, - dir, dir.Append("minidump.dmp"))); + dir, dir.Append("minidump.dmp"), + "base")); // Check to see if the values are still correct and that the file was // written with the right data. @@ -116,8 +120,9 @@ TEST_F(ChromeCollectorTest, File) { EXPECT_TRUE(meta.find("value1=abcdefghij") != std::string::npos); EXPECT_TRUE(meta.find("value2=12345") != std::string::npos); EXPECT_TRUE(meta.find("value3=ok") != std::string::npos); - ExpectFileEquals("12345\n789\n12345", "foo.txt.other"); - file_util::Delete(dir.Append("foo.txt.other"), false); + ExpectFileEquals("12345\n789\n12345", + dir.Append("base-foo.txt.other").value().c_str()); + file_util::Delete(dir.Append("base-foo.txt.other"), false); } int main(int argc, char **argv) { diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index b433bec65..d39d6f48a 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -39,6 +39,8 @@ static const char kLeaveCoreFile[] = "/root/.leave_core"; static const char kLsbRelease[] = "/etc/lsb-release"; static const char kShellPath[] = "/bin/sh"; static const char kSystemCrashPath[] = "/var/spool/crash"; +static const char kUploadVarPrefix[] = "upload_var_"; +static const char kUploadFilePrefix[] = "upload_file_"; // Normally this path is not used. Unfortunately, there are a few edge cases // where we need this. Any process that runs as kDefaultUserName that crashes // is consider a "user crash". That includes the initial Chrome browser that @@ -479,6 +481,18 @@ void CrashCollector::AddCrashMetaData(const std::string &key, extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str())); } +void CrashCollector::AddCrashMetaUploadFile(const std::string &key, + const std::string &path) { + if (!path.empty()) + AddCrashMetaData(kUploadFilePrefix + key, path); +} + +void CrashCollector::AddCrashMetaUploadData(const std::string &key, + const std::string &value) { + if (!value.empty()) + AddCrashMetaData(kUploadVarPrefix + key, value); +} + void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, const std::string &exec_name, const std::string &payload_path) { diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 4cbe1b977..19ec97b03 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -137,6 +137,16 @@ class CrashCollector { // "\n" characters. Value must not contain "\n" characters. void AddCrashMetaData(const std::string &key, const std::string &value); + // Add a file to be uploaded to the crash reporter server. The file must + // persist until the crash report is sent; ideally it should live in the same + // place as the .meta file, so it can be cleaned up automatically. + void AddCrashMetaUploadFile(const std::string &key, const std::string &path); + + // Add non-standard meta data to the crash metadata file. + // Data added though this call will be uploaded to the crash reporter server, + // appearing as a form field. + void AddCrashMetaUploadData(const std::string &key, const std::string &value); + // Write a file of metadata about crash. void WriteCrashMetaData(const base::FilePath &meta_path, const std::string &exec_name, diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 6bebb6ea8..72205cffc 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -240,16 +240,25 @@ get_kind() { } get_key_value() { - local file=$1 key=$2 value + local file="$1" key="$2" value if [ -f "${file}" ]; then # Return the first entry. There shouldn't be more than one anyways. - value=$(awk -F= '/^'"${key}"'[[:space:]]*=/ { print $NF }' "${file}") + # Substr at length($1) + 2 skips past the key and following = sign (awk + # uses 1-based indexes), but preserves embedded = characters. + value=$(sed -n "/^${key}[[:space:]]*=/{s:^[^=]*=::p;q}" "${file}") fi echo "${value:-undefined}" } +get_keys() { + local file="$1" regex="$2" + + awk -F'[[:space:]=]' -vregex="${regex}" \ + 'match($1, regex) { print $1 }' "${file}" +} + # Return the board name. get_board() { get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_BOARD" @@ -286,6 +295,46 @@ send_crash() { local log="$(get_key_value "${meta_path}" "log")" local sig="$(get_key_value "${meta_path}" "sig")" local send_payload_size="$(stat --printf=%s "${report_payload}" 2>/dev/null)" + local product="$(get_key_value "${meta_path}" "upload_var_prod")" + local version="$(get_key_value "${meta_path}" "upload_var_ver")" + local upload_prefix="$(get_key_value "${meta_path}" "upload_prefix")" + + set -- \ + -F "write_payload_size=${write_payload_size}" \ + -F "send_payload_size=${send_payload_size}" + if [ "${sig}" != "undefined" ]; then + set -- "$@" \ + -F "sig=${sig}" \ + -F "sig2=${sig}" + fi + if [ "${log}" != "undefined" ]; then + set -- "$@" \ + -F "log=@${log}" + fi + + if [ "${upload_prefix}" = "undefined" ]; then + upload_prefix="" + fi + + # Grab any variable that begins with upload_. + local v + for k in $(get_keys "${meta_path}" "^upload_"); do + v="$(get_key_value "${meta_path}" "${k}")" + case ${k} in + # Product & version are handled separately. + upload_var_prod) ;; + upload_var_ver) ;; + upload_var_*) set -- "$@" -F "${upload_prefix}${k#upload_var_}=${v}" ;; + upload_file_*) set -- "$@" -F "${upload_prefix}${k#upload_file_}=@${v}" ;; + esac + done + + # When uploading Chrome reports we need to report the right product and + # version, so allow the meta file to override these values. + if [ "${product}" = "undefined" ]; then + product=${CHROMEOS_PRODUCT} + version=${chromeos_version} + fi local image_type if is_test_image; then @@ -307,38 +356,32 @@ send_crash() { boot_mode="dev" fi - local extra_key1="write_payload_size" - local extra_value1="${write_payload_size}" - local extra_key2="send_payload_size" - local extra_value2="${send_payload_size}" - if [ "${sig}" != "undefined" ]; then - extra_key1="sig" - extra_value1="${sig}" - extra_key2="sig2" - extra_value2="${sig}" - elif [ "${log}" != "undefined" ]; then - # Upload a log file if it was specified. - extra_key1="log" - extra_value1="@${log}" - fi - local error_type="$(get_key_value "${meta_path}" "error_type")" [ "${error_type}" = "undefined" ] && error_type= lecho "Sending crash:" + if [ "${product}" != "${CHROMEOS_PRODUCT}" ]; then + lecho " Sending crash report on behalf of ${product}" + fi lecho " Scheduled to send in ${sleep_time}s" lecho " Metadata: ${meta_path} (${kind})" lecho " Payload: ${report_payload}" - lecho " Version: ${chromeos_version}" + lecho " Version: ${version}" [ -n "${image_type}" ] && lecho " Image type: ${image_type}" [ -n "${boot_mode}" ] && lecho " Boot mode: ${boot_mode}" if is_mock; then - lecho " Product: ${CHROMEOS_PRODUCT}" + lecho " Product: ${product}" lecho " URL: ${url}" lecho " Board: ${board}" lecho " HWClass: ${hwclass}" - lecho " ${extra_key1}: ${extra_value1}" - lecho " ${extra_key2}: ${extra_value2}" + lecho " write_payload_size: ${write_payload_size}" + lecho " send_payload_size: ${send_payload_size}" + if [ "${log}" != "undefined" ]; then + lecho " log: @${log}" + fi + if [ "${sig}" != "undefined" ]; then + lecho " sig: ${sig}" + fi fi lecho " Exec name: ${exec_name}" [ -n "${error_type}" ] && lecho " Error type: ${error_type}" @@ -369,8 +412,8 @@ send_crash() { set +e curl "${url}" ${proxy:+--proxy "$proxy"} \ --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \ - -F "prod=${CHROMEOS_PRODUCT}" \ - -F "ver=${chromeos_version}" \ + -F "prod=${product}" \ + -F "ver=${version}" \ -F "upload_file_${kind}=@${report_payload}" \ -F "board=${board}" \ -F "hwclass=${hwclass}" \ @@ -378,9 +421,10 @@ send_crash() { ${image_type:+-F "image_type=${image_type}"} \ ${boot_mode:+-F "boot_mode=${boot_mode}"} \ ${error_type:+-F "error_type=${error_type}"} \ - -F "${extra_key1}=${extra_value1}" \ - -F "${extra_key2}=${extra_value2}" \ - -F "guid=<${CONSENT_ID}" -o "${report_id}" 2>"${curl_stderr}" + -F "guid=<${CONSENT_ID}" \ + -o "${report_id}" \ + "$@" \ + 2>"${curl_stderr}" curl_result=$? set -e From 8b05c32b5dd3a95dde313b3b2ae4fb22ed91a8bd Mon Sep 17 00:00:00 2001 From: Yuly Novikov Date: Thu, 11 Jul 2013 17:27:51 -0400 Subject: [PATCH 129/225] crash_collector: Collect i915_error_state for chrome crashes Since crash_collector runs as chronos for chrome crashes, it doesn't have permission to read i915_error_state. Switch to getting it from debugd instead of reading directly. Also, collect it for all chrome crashes. BUG=chromium:219121 TEST=Kill chrome, see the log gathered. Uploaded report id 7cbae7f70da9bd3e. Change-Id: Ic74abea2fc86b16c5a7f0f11df73137d93e5220c Reviewed-on: https://gerrit.chromium.org/gerrit/61606 Reviewed-by: Albert Chaulk Commit-Queue: Yuly Novikov Tested-by: Yuly Novikov --- crash_reporter/chrome_collector.cc | 95 ++++++++++++++++++++++++- crash_reporter/crash_reporter_logs.conf | 9 --- 2 files changed, 92 insertions(+), 12 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 2c9ae6e81..8f5ddb3ff 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -14,8 +14,13 @@ #include #include "chromeos/process.h" #include "chromeos/syslog_logging.h" +#include "chromeos/dbus/dbus.h" +#include "chromeos/dbus/service_constants.h" const char kDefaultMinidumpName[] = "upload_file_minidump"; +const char kTarPath[] = "/bin/tar"; +// From //net/crash/collector/collector.h +const int kDefaultMaxUploadBytes = 1024 * 1024; namespace { @@ -31,6 +36,78 @@ bool GetDelimitedString(const std::string &str, char ch, size_t offset, return true; } +bool GetDriErrorState(const chromeos::dbus::Proxy &proxy, + const FilePath &error_state_path) { + chromeos::glib::ScopedError error; + gchar *error_state = NULL; + if (!dbus_g_proxy_call(proxy.gproxy(), debugd::kGetLog, + &chromeos::Resetter(&error).lvalue(), + G_TYPE_STRING, "i915_error_state", G_TYPE_INVALID, + G_TYPE_STRING, &error_state, G_TYPE_INVALID)) { + LOG(ERROR) << "Error performing D-Bus proxy call " + << "'" << debugd::kGetLog << "'" + << ": " << (error ? error->message : ""); + g_free(error_state); + return false; + } + + std::string error_state_str(error_state); + g_free(error_state); + + if(error_state_str == "") + return false; + + int written; + written = file_util::WriteFile(error_state_path, error_state_str.c_str(), + error_state_str.length()); + + if (written < 0 || (size_t)written != error_state_str.length()) { + LOG(ERROR) << "Could not write file " << error_state_path.value(); + file_util::Delete(error_state_path, false); + return false; + } + + return true; +} + +bool GetAdditionalLogs(const FilePath &log_path) { + chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); + if (!dbus.HasConnection()) { + LOG(ERROR) << "Error connecting to system D-Bus"; + return false; + } + + chromeos::dbus::Proxy proxy(dbus, + debugd::kDebugdServiceName, + debugd::kDebugdServicePath, + debugd::kDebugdInterface); + if (!proxy) { + LOG(ERROR) << "Error creating D-Bus proxy to interface " + << "'" << debugd::kDebugdServiceName << "'"; + return false; + } + + FilePath error_state_path = log_path.DirName().Append("i915_error_state.log"); + if (!GetDriErrorState(proxy, error_state_path)) + return false; + + chromeos::ProcessImpl tar_process; + tar_process.AddArg(kTarPath); + tar_process.AddArg("cfz"); + tar_process.AddArg(log_path.value()); + tar_process.AddStringOption("-C", log_path.DirName().value()); + tar_process.AddArg(error_state_path.BaseName().value()); + int res = tar_process.Run(); + + file_util::Delete(error_state_path, false); + + if (res || !file_util::PathExists(log_path)) { + LOG(ERROR) << "Could not tar file " << log_path.value(); + return false; + } + + return true; +} } //namespace @@ -61,7 +138,7 @@ bool ChromeCollector::HandleCrash(const std::string &file_path, std::string dump_basename = FormatDumpBasename(exe_name, time(NULL), pid); FilePath meta_path = GetCrashPath(dir, dump_basename, "meta"); FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp"); - FilePath log_path = GetCrashPath(dir, dump_basename, "log"); + FilePath log_path = GetCrashPath(dir, dump_basename, "log.tgz"); std::string data; if (!file_util::ReadFileToString(FilePath(file_path), &data)) { @@ -74,8 +151,20 @@ bool ChromeCollector::HandleCrash(const std::string &file_path, return false; } - if (GetLogContents(FilePath(log_config_path_), exe_name, log_path)) - AddCrashMetaData("log", log_path.value()); + if (GetAdditionalLogs(log_path)) { + int64 minidump_size = 0; + int64 log_size = 0; + if (file_util::GetFileSize(minidump_path, &minidump_size) && + file_util::GetFileSize(log_path, &log_size) && + minidump_size > 0 && log_size > 0 && + minidump_size + log_size < kDefaultMaxUploadBytes) { + AddCrashMetaData("log", log_path.value()); + } else { + LOG(INFO) << "Skipping logs upload to prevent discarding minidump " + "because of report size limit < " << minidump_size + log_size; + file_util::Delete(log_path, false); + } + } // We're done. WriteCrashMetaData(meta_path, exe_name, minidump_path.value()); diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 808e71d24..9c1a531ec 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -31,15 +31,6 @@ crash_reporter-user-collection:echo "===ps output==="; ps axw -o user,pid,%cpu,% # run for kernel errors reported through udev events. crash_reporter-udev-collection-change-card0-drm:for dri in /sys/kernel/debug/dri/*; do echo "===$dri/i915_error_state==="; cat $dri/i915_error_state; done -# TODO(mkrebs,sabercrombie): Because these can be quite large (600KB -# compressed), remove this when GPU hangs are no longer an issue, and/or we -# start collecting "chrome" crashes for normal images. -# Collect i915 error state for Chrome crashes as well. See -# crosbug.com/36979. These can be big (i.e. megabytes), so compress them with -# gzip. Note that the file attached to the crash report will have a ".log" -# extension, but will have been gzip'ed. -chrome:for dri in /sys/kernel/debug/dri/*; do echo "===$dri/i915_error_state==="; cat $dri/i915_error_state; done | gzip -c - # When trackpad driver cyapa detects some abnormal behavior, we collect # additional logs from kernel messages. crash_reporter-udev-collection-change--i2c-cyapa:/usr/sbin/kernel_log_collector.sh cyapa 30 From 9c9e1c43d3008ce3310f75df243863b03658dc63 Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Thu, 18 Jul 2013 15:57:11 -0700 Subject: [PATCH 130/225] crash: Remove references to low-battery state file. Remove references to the low-battery state file formerly written by powerd. The system shuts down immediately when it enters the low-battery state. BUG=chromium:259189 TEST=ran unit tests Change-Id: I2122210b448af58fb4ecac2c234b9faa9d231e2b Reviewed-on: https://gerrit.chromium.org/gerrit/62578 Reviewed-by: Chris Masone Tested-by: Daniel Erat Commit-Queue: Daniel Erat --- crash_reporter/unclean_shutdown_collector.cc | 19 +++---------------- crash_reporter/unclean_shutdown_collector.h | 2 -- .../unclean_shutdown_collector_test.cc | 16 +--------------- 3 files changed, 4 insertions(+), 33 deletions(-) diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc index b1462ad37..14cc8f415 100644 --- a/crash_reporter/unclean_shutdown_collector.cc +++ b/crash_reporter/unclean_shutdown_collector.cc @@ -14,16 +14,13 @@ static const char kUncleanShutdownFile[] = static const char kPowerdTracePath[] = "/var/lib/power_manager"; // Presence of this file indicates that the system was suspended static const char kPowerdSuspended[] = "powerd_suspended"; -// Presence of this file indicates that the battery was critically low. -static const char kPowerdLowBattery[] = "powerd_low_battery"; using base::FilePath; UncleanShutdownCollector::UncleanShutdownCollector() : unclean_shutdown_file_(kUncleanShutdownFile), powerd_trace_path_(kPowerdTracePath), - powerd_suspended_file_(powerd_trace_path_.Append(kPowerdSuspended)), - powerd_low_battery_file_(powerd_trace_path_.Append(kPowerdLowBattery)) { + powerd_suspended_file_(powerd_trace_path_.Append(kPowerdSuspended)) { } UncleanShutdownCollector::~UncleanShutdownCollector() { @@ -45,9 +42,8 @@ bool UncleanShutdownCollector::DeleteUncleanShutdownFiles() { << unclean_shutdown_file_; return false; } - // Delete power manager trace files if they exist. + // Delete power manager state file if it exists. file_util::Delete(powerd_suspended_file_, false); - file_util::Delete(powerd_low_battery_file_, false); return true; } @@ -74,21 +70,12 @@ bool UncleanShutdownCollector::Disable() { return DeleteUncleanShutdownFiles(); } -bool UncleanShutdownCollector::DeadBatteryCausedUncleanShutdown() -{ +bool UncleanShutdownCollector::DeadBatteryCausedUncleanShutdown() { // Check for case of battery running out while suspended. if (file_util::PathExists(powerd_suspended_file_)) { LOG(INFO) << "Unclean shutdown occurred while suspended. Not counting " << "toward unclean shutdown statistic."; return true; } - // Check for case of battery running out after resuming from a low-battery - // suspend. - if (file_util::PathExists(powerd_low_battery_file_)) { - LOG(INFO) << "Unclean shutdown occurred while running with battery " - << "critically low. Not counting toward unclean shutdown " - << "statistic."; - return true; - } return false; } diff --git a/crash_reporter/unclean_shutdown_collector.h b/crash_reporter/unclean_shutdown_collector.h index 37eb4dffb..7a5b669e2 100644 --- a/crash_reporter/unclean_shutdown_collector.h +++ b/crash_reporter/unclean_shutdown_collector.h @@ -30,7 +30,6 @@ class UncleanShutdownCollector : public CrashCollector { private: friend class UncleanShutdownCollectorTest; FRIEND_TEST(UncleanShutdownCollectorTest, EnableCannotWrite); - FRIEND_TEST(UncleanShutdownCollectorTest, CollectDeadBatteryRunningLow); FRIEND_TEST(UncleanShutdownCollectorTest, CollectDeadBatterySuspended); bool DeleteUncleanShutdownFiles(); @@ -42,7 +41,6 @@ class UncleanShutdownCollector : public CrashCollector { const char *unclean_shutdown_file_; base::FilePath powerd_trace_path_; base::FilePath powerd_suspended_file_; - base::FilePath powerd_low_battery_file_; }; #endif // _CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_ diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc index 1bb0c9431..21e2aaa77 100644 --- a/crash_reporter/unclean_shutdown_collector_test.cc +++ b/crash_reporter/unclean_shutdown_collector_test.cc @@ -16,7 +16,6 @@ static int s_crashes = 0; static bool s_metrics = true; static const char kTestDirectory[] = "test"; -static const char kTestLowBattery[] = "test/low_battery"; static const char kTestSuspended[] = "test/suspended"; static const char kTestUnclean[] = "test/unclean"; @@ -40,9 +39,8 @@ class UncleanShutdownCollectorTest : public ::testing::Test { test_unclean_ = FilePath(kTestUnclean); collector_.unclean_shutdown_file_ = kTestUnclean; file_util::Delete(test_unclean_, true); - // Set up alternate power manager tracing files as well + // Set up an alternate power manager state file as well collector_.powerd_suspended_file_ = FilePath(kTestSuspended); - collector_.powerd_low_battery_file_ = FilePath(kTestLowBattery); chromeos::ClearLog(); } protected: @@ -87,18 +85,6 @@ TEST_F(UncleanShutdownCollectorTest, CollectFalse) { ASSERT_EQ(0, s_crashes); } -TEST_F(UncleanShutdownCollectorTest, CollectDeadBatteryRunningLow) { - ASSERT_TRUE(collector_.Enable()); - ASSERT_TRUE(file_util::PathExists(test_unclean_)); - file_util::WriteFile(collector_.powerd_low_battery_file_, "", 0); - ASSERT_FALSE(collector_.Collect()); - ASSERT_FALSE(file_util::PathExists(test_unclean_)); - ASSERT_FALSE(file_util::PathExists(collector_.powerd_low_battery_file_)); - ASSERT_EQ(0, s_crashes); - ASSERT_TRUE(FindLog( - "Unclean shutdown occurred while running with battery critically low.")); -} - TEST_F(UncleanShutdownCollectorTest, CollectDeadBatterySuspended) { ASSERT_TRUE(collector_.Enable()); ASSERT_TRUE(file_util::PathExists(test_unclean_)); From 8ec93fa0d03579b3271b08a1542daea2957af734 Mon Sep 17 00:00:00 2001 From: Yuly Novikov Date: Thu, 18 Jul 2013 20:31:47 -0400 Subject: [PATCH 131/225] crash_collector: Handle compressed i915_error_state for chrome crashes i915_error_state was previously truncated by debugd, and will be compressed in the future to prevent that. Because of compression, it will now be received in base64 format, so to reduce it's size, decode it back. Also, use xz compression, since it's more compact. The second compression is a preparation for adding task states to the logs. BUG=chromium:219121 TEST=Kill chrome, see the log gathered. Uploaded report id 5e5a6f9bf6ff4b5f. Change-Id: I316cdaef8f8f42d4ac839f7c880960cdf6d7a1e8 Reviewed-on: https://gerrit.chromium.org/gerrit/62568 Reviewed-by: Albert Chaulk Tested-by: Yuly Novikov Commit-Queue: Yuly Novikov --- crash_reporter/chrome_collector.cc | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 8f5ddb3ff..4b0cd2425 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -4,6 +4,7 @@ #include "crash-reporter/chrome_collector.h" +#include #include #include #include @@ -54,14 +55,26 @@ bool GetDriErrorState(const chromeos::dbus::Proxy &proxy, std::string error_state_str(error_state); g_free(error_state); - if(error_state_str == "") + if (error_state_str == "") return false; - int written; - written = file_util::WriteFile(error_state_path, error_state_str.c_str(), - error_state_str.length()); + const char kBase64Header[] = ": "; + const size_t kBase64HeaderLength = sizeof(kBase64Header) - 1; + if (error_state_str.compare(0, kBase64HeaderLength, kBase64Header)) { + LOG(ERROR) << "i915_error_state is missing base64 header"; + return false; + } - if (written < 0 || (size_t)written != error_state_str.length()) { + gsize len; + guchar *decoded_error_state = + g_base64_decode(error_state_str.c_str() + kBase64HeaderLength, &len); + + int written; + written = file_util::WriteFile(error_state_path, + reinterpret_cast(decoded_error_state), len); + g_free(decoded_error_state); + + if (written < 0 || (gsize)written != len) { LOG(ERROR) << "Could not write file " << error_state_path.value(); file_util::Delete(error_state_path, false); return false; @@ -87,13 +100,14 @@ bool GetAdditionalLogs(const FilePath &log_path) { return false; } - FilePath error_state_path = log_path.DirName().Append("i915_error_state.log"); + FilePath error_state_path = + log_path.DirName().Append("i915_error_state.log.xz"); if (!GetDriErrorState(proxy, error_state_path)) return false; chromeos::ProcessImpl tar_process; tar_process.AddArg(kTarPath); - tar_process.AddArg("cfz"); + tar_process.AddArg("cfJ"); tar_process.AddArg(log_path.value()); tar_process.AddStringOption("-C", log_path.DirName().value()); tar_process.AddArg(error_state_path.BaseName().value()); @@ -138,7 +152,7 @@ bool ChromeCollector::HandleCrash(const std::string &file_path, std::string dump_basename = FormatDumpBasename(exe_name, time(NULL), pid); FilePath meta_path = GetCrashPath(dir, dump_basename, "meta"); FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp"); - FilePath log_path = GetCrashPath(dir, dump_basename, "log.tgz"); + FilePath log_path = GetCrashPath(dir, dump_basename, "log.tar.xz"); std::string data; if (!file_util::ReadFileToString(FilePath(file_path), &data)) { From 2aa552ad439013dde0cbcdebbde1f2fdccaef828 Mon Sep 17 00:00:00 2001 From: Yuly Novikov Date: Wed, 24 Jul 2013 10:49:49 -0400 Subject: [PATCH 132/225] crash_collector: Collect kernel task states for chrome crashes When handling chrome crash: call debugd to write kernel task states into syslog, parse only the relevant task states out of syslog, pack them into the log file to be uploaded. BUG=chromium:244916 TEST=Kill chrome, see the task states in crash log, on Link, Snow and Alex. Change-Id: Ib8aad79622886581ae5ce50c03097f01db9153a7 Reviewed-on: https://gerrit.chromium.org/gerrit/63205 Reviewed-by: Albert Chaulk Reviewed-by: Balazs Engedy Tested-by: Yuly Novikov Commit-Queue: Yuly Novikov --- crash_reporter/chrome_collector.cc | 260 ++++++++++++++++++++++++++++- 1 file changed, 258 insertions(+), 2 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 4b0cd2425..55a7a5351 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -4,15 +4,20 @@ #include "crash-reporter/chrome_collector.h" +#include #include +#define __STDC_FORMAT_MACROS +#include #include #include #include #include #include +#include #include #include +#include #include "chromeos/process.h" #include "chromeos/syslog_logging.h" #include "chromeos/dbus/dbus.h" @@ -23,6 +28,33 @@ const char kTarPath[] = "/bin/tar"; // From //net/crash/collector/collector.h const int kDefaultMaxUploadBytes = 1024 * 1024; +const char kHeader[] = "kernel_task_states"; +const char kEnter[] = "enter"; +const char kLeave[] = "leave"; +const char kSyslogPath[] = "/var/log/messages"; +const char kFirstTwoColumnsRE[] = "\\S+\\s+\\S+\\s+"; +const char kIdent[] = "crash_reporter"; +const char kSpacesRE[] = "\\s+"; +const char kKernelIdentRE[] = "kernel:"; +const char kKernelTimestampRE[] = "\\[\\s*\\d+\\.\\d+\\]"; +const char kTaskNameRE[] = "(.{15})"; +// kernel's TASK_STATE_TO_CHAR_STR + '?' +const char kTaskStateRE[] = "([RSDTtZXxKW?])"; +const char kHexNumRE[] = "([[:xdigit:]]+)"; +const char kNumRE[] = "([[:digit:]]+)"; +const char kRunning32[] = "running "; +const char kRunning64[] = " running task "; +// This regex matches symbol+offset as printed by printk(%pB) or (%pS). +// See https://www.kernel.org/doc/Documentation/printk-formats.txt +// As noted in http://sourceware.org/binutils/docs-2.23.1/as/Symbol-Names.html +// symbols may contain periods and dollars, like "scm_recv.isra.18+0x61/0xdf". +// Strangely, occasionally there is an additional string following the symbol, +// like "qcusbnet_get+0x3e4/0x1e46 [gobi]". +// Those lines will be missing in the output, as parsing them is troublesome. +// Sometimes the address is printed and not the symbol. Those are ignored too. +const char kSymbolAndOffsetRE[] = + "([[:alpha:]._$][[:alnum:]._$]*)\\+0x([[:xdigit:]]+)/0x([[:xdigit:]]+)"; + namespace { // Extract a string delimited by the given character, from the given offset @@ -83,6 +115,216 @@ bool GetDriErrorState(const chromeos::dbus::Proxy &proxy, return true; } +bool LogKernelTaskStates(const chromeos::dbus::Proxy &proxy, + const std::string &task_states_id) { + // Wrap task states with unique id - header + LOG(INFO) << kHeader << " " << task_states_id.c_str() << " " << kEnter; + + // Trigger asynchronous logging of task states + chromeos::glib::ScopedError error; + if (!dbus_g_proxy_call(proxy.gproxy(), debugd::kLogKernelTaskStates, + &chromeos::Resetter(&error).lvalue(), + G_TYPE_INVALID, G_TYPE_INVALID)) { + LOG(ERROR) << "Error performing D-Bus proxy call " + << "'" << debugd::kLogKernelTaskStates << "'" + << ": " << (error ? error->message : ""); + return false; + } + + // Give kernel a second to finish the asynchronous logging + sleep(1); + // Wrap task states with unique id - footer + LOG(INFO) << kHeader << " " << task_states_id.c_str() << " " << kLeave; + + return true; +} + +bool GetKernelTaskStates(const chromeos::dbus::Proxy &proxy, + std::vector *output) { + std::string task_states_id = StringPrintf("%016"PRIX64, base::RandUint64()); + + if (!LogKernelTaskStates(proxy, task_states_id)) + return false; + + std::ifstream syslog_stream(kSyslogPath, std::ios::in); + if (!syslog_stream.is_open()) { + LOG(ERROR) << "Couldn't open syslog for reading"; + return false; + } + + std::string ident_re = StringPrintf("%s\\[%d\\]:\\s+", kIdent, getpid()); + // 2013-07-05T11:52:55.530503-07:00 localhost crash_reporter[9349]: + // kernel_task_states 33F114E753768B70 enter + pcrecpp::RE enter_re(kFirstTwoColumnsRE + ident_re + kHeader + " " + + task_states_id + " " + kEnter); + // 2013-07-05T11:52:56.556177-07:00 localhost crash_reporter[9349]: + // kernel_task_states 33F114E753768B70 leave + pcrecpp::RE leave_re(kFirstTwoColumnsRE + ident_re + kHeader + " " + + task_states_id + " " + kLeave); + // 2013-06-21T21:18:14.855071+00:00 localhost kernel: [362142.027766] + // NSS SSL ThreadW S 804f8e28 0 5995 1016 0x00000000 + pcrecpp::RE task_header_re(std::string(kFirstTwoColumnsRE) + + kKernelIdentRE + kSpacesRE + + kKernelTimestampRE + kSpacesRE + + kTaskNameRE + kSpacesRE + + kTaskStateRE + kSpacesRE + + kHexNumRE + kSpacesRE + + kNumRE + kSpacesRE + + kNumRE + kSpacesRE + + kNumRE + kSpacesRE + + "0x" + kHexNumRE); + // 2013-06-21T21:18:14.815190+00:00 localhost kernel: [362142.016915] + // rsyslogd R running 0 340 1 0x00000000 + pcrecpp::RE task_header_run32_re(std::string(kFirstTwoColumnsRE) + + kKernelIdentRE + kSpacesRE + + kKernelTimestampRE + kSpacesRE + + kTaskNameRE + kSpacesRE + + kTaskStateRE + kSpacesRE + + kRunning32 + kSpacesRE + + kNumRE + kSpacesRE + + kNumRE + kSpacesRE + + kNumRE + kSpacesRE + + "0x" + kHexNumRE); + // 2013-06-21T14:11:18.718810-07:00 localhost kernel: [ 656.217932] + // kworker/2:1 R running task 0 54 2 0x00000000 + pcrecpp::RE task_header_run64_re(std::string(kFirstTwoColumnsRE) + + kKernelIdentRE + kSpacesRE + + kKernelTimestampRE + kSpacesRE + + kTaskNameRE + kSpacesRE + + kTaskStateRE + kSpacesRE + + kRunning64 + kSpacesRE + + kNumRE + kSpacesRE + + kNumRE + kSpacesRE + + kNumRE + kSpacesRE + + "0x" + kHexNumRE); + std::string address_re = std::string("\\[<") + kHexNumRE + ">\\]"; + std::string braced_sym_re = std::string("\\(") + kSymbolAndOffsetRE + "\\)"; + // 2013-07-05T11:53:20.141355-07:00 localhost kernel: [ 169.476749] + // [<8002fb74>] (do_wait+0x1a8/0x248) from [<80030c48>] (sys_wait4+0xbc/0xf8) + pcrecpp::RE arm_stack_re(std::string(kFirstTwoColumnsRE) + + kKernelIdentRE + kSpacesRE + + kKernelTimestampRE + kSpacesRE + + address_re + kSpacesRE + + braced_sym_re + kSpacesRE + + "from" + kSpacesRE + + address_re + kSpacesRE + + braced_sym_re); + // 2013-06-21T14:12:34.457315-07:00 localhost kernel: [ 731.965907] + // [] ? system_call_fastpath+0x16/0x1b + pcrecpp::RE x86_stack_re(std::string(kFirstTwoColumnsRE) + + kKernelIdentRE + kSpacesRE + + kKernelTimestampRE + kSpacesRE + + address_re + kSpacesRE + + "(\\?\\s+)?" + + kSymbolAndOffsetRE); + + bool enter_matched = false; + bool leave_matched = false; + uint64 prev_address = 0; + std::string line; + while (std::getline(syslog_stream, line)) { + if (leave_re.FullMatch(line)) { + if (!enter_matched) { + LOG(ERROR) << "Task states header not found before footer"; + return false; + } + leave_matched = true; + break; // Stop processing input after footer + } + + if (enter_re.FullMatch(line)) { + if (enter_matched) { + LOG(ERROR) << "Task states header encountered twice"; + return false; + } + enter_matched = true; + continue; + } + + if(!enter_matched) + continue; // Skip lines before header + + std::string task_name, task_state; + uint64 address1, address2; + unsigned long free_stack; + pid_t pid, ppid; + unsigned long flags; + std::string sym1, sym2; + uint64 offset1a, offset1b, offset2a, offset2b; + std::string question; + + if (task_header_re.FullMatch(line, &task_name, &task_state, + pcrecpp::Hex(&address1), &free_stack, &pid, + &ppid, pcrecpp::Hex(&flags))) { + prev_address = 0; + output->push_back(StringPrintf("%s %s %016"PRIx64" %5lu %5d %6d 0x%08lx", + task_name.c_str(), task_state.c_str(), + address1, free_stack, pid, ppid, flags)); + } else if (task_header_run32_re.FullMatch(line, &task_name, &task_state, + &free_stack, &pid, &ppid, + pcrecpp::Hex(&flags)) || + task_header_run64_re.FullMatch(line, &task_name, &task_state, + &free_stack, &pid, &ppid, + pcrecpp::Hex(&flags))) { + prev_address = 0; + output->push_back(StringPrintf("%s %s %s %5lu %5d %6d 0x%08lx", + task_name.c_str(), task_state.c_str(), + kRunning64, free_stack, pid, ppid, flags)); + } else if (arm_stack_re.FullMatch(line, pcrecpp::Hex(&address1), &sym1, + pcrecpp::Hex(&offset1a), + pcrecpp::Hex(&offset1b), + pcrecpp::Hex(&address2), &sym2, + pcrecpp::Hex(&offset2a), + pcrecpp::Hex(&offset2b))) { + if (prev_address != address1) { + output->push_back(StringPrintf("%s+0x%"PRIx64"/0x%"PRIx64, + sym1.c_str(), offset1a, offset1b)); + } + output->push_back(StringPrintf("%s+0x%"PRIx64"/0x%"PRIx64, + sym2.c_str(), offset2a, offset2b)); + prev_address = address2; + } else if (x86_stack_re.FullMatch(line, pcrecpp::Hex(&address1), &question, + &sym1, pcrecpp::Hex(&offset1a), + pcrecpp::Hex(&offset1b))) { + output->push_back(StringPrintf("%s%s+0x%"PRIx64"/0x%"PRIx64, + question.c_str(), sym1.c_str(), + offset1a, offset1b)); + } + } + syslog_stream.close(); + + if(!enter_matched || !leave_matched) { + LOG(ERROR) << "Task states header or footer not found"; + return false; + } + + return true; +} + +bool WriteKernelTaskStates(const chromeos::dbus::Proxy &proxy, + const FilePath &task_states_path) { + std::ofstream task_states_stream(task_states_path.value().c_str(), + std::ios::out); + if (!task_states_stream.is_open()) { + LOG(ERROR) << "Could not write file " << task_states_path.value(); + return false; + } + + std::vector task_sates; + if (!GetKernelTaskStates(proxy, &task_sates)) { + file_util::Delete(task_states_path, false); + return false; + } + + for (std::vector::iterator it = task_sates.begin(); + it != task_sates.end(); ++it) { + task_states_stream << *it << std::endl; + } + task_states_stream.close(); + + return true; +} + bool GetAdditionalLogs(const FilePath &log_path) { chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); if (!dbus.HasConnection()) { @@ -102,7 +344,17 @@ bool GetAdditionalLogs(const FilePath &log_path) { FilePath error_state_path = log_path.DirName().Append("i915_error_state.log.xz"); - if (!GetDriErrorState(proxy, error_state_path)) + bool has_dri_error_state = false; + if (GetDriErrorState(proxy, error_state_path)) + has_dri_error_state = true; + + FilePath task_states_path = log_path.DirName().Append("task_states.log"); + bool has_task_states = false; + if (WriteKernelTaskStates(proxy, task_states_path)) { + has_task_states = true; + } + + if (!has_dri_error_state && !has_task_states) return false; chromeos::ProcessImpl tar_process; @@ -110,10 +362,14 @@ bool GetAdditionalLogs(const FilePath &log_path) { tar_process.AddArg("cfJ"); tar_process.AddArg(log_path.value()); tar_process.AddStringOption("-C", log_path.DirName().value()); - tar_process.AddArg(error_state_path.BaseName().value()); + if (has_dri_error_state) + tar_process.AddArg(error_state_path.BaseName().value()); + if (has_task_states) + tar_process.AddArg(task_states_path.BaseName().value()); int res = tar_process.Run(); file_util::Delete(error_state_path, false); + file_util::Delete(task_states_path, false); if (res || !file_util::PathExists(log_path)) { LOG(ERROR) << "Could not tar file " << log_path.value(); From c3f7973c24b97f48b751cfd7194dbef26d35f511 Mon Sep 17 00:00:00 2001 From: Rohit Makasana Date: Mon, 12 Aug 2013 12:58:05 +0530 Subject: [PATCH 133/225] Adding a lock mechanism to crash_sender. This is to avoid situations when crash_sender is already running and while test crash reporting, we execute crash_sender manually. BUG=None TEST=Manually Change-Id: I3beea8d503940d7a1af24c8f16eebb365d447434 Reviewed-on: https://gerrit.chromium.org/gerrit/65556 Reviewed-by: Mike Frysinger Commit-Queue: Rohit Makasana Reviewed-by: Rohit Makasana Tested-by: Rohit Makasana --- crash_reporter/crash_sender | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 72205cffc..0422ca20f 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -18,6 +18,9 @@ CLEAN_UP_RUN_FILE=0 # contents includes our machine's anonymized guid. CONSENT_ID="/home/chronos/Consent To Send Stats" +# Crash sender lock in case the sender is already running. +CRASH_SENDER_LOCK="/var/lock/crash_sender" + # Path to file that indicates a crash test is currently running. CRASH_TEST_IN_PROGRESS_FILE="/tmp/crash-test-in-progress" @@ -569,4 +572,10 @@ main() { done } +( +if ! flock -n 9; then + lecho "crash_sender is already running; quitting." + exit 1 +fi main +) 9>"${CRASH_SENDER_LOCK}" From 49a7c92023c7a30615114cf052401975dcb35bdc Mon Sep 17 00:00:00 2001 From: Albert Chaulk Date: Wed, 14 Aug 2013 14:46:58 -0700 Subject: [PATCH 134/225] Append all crashes to Chrome's crash log Currently chrome://crashes reports no crashes due to the switch to using crash_reporter. This adds reports of all crashes, with the report ID and product (Chrome or Chrome OS) to Chrome's log. BUG=chromium:210624 TEST=none Change-Id: I7e47f6af0374095250830f46b693ac2fa053fa54 Reviewed-on: https://gerrit.chromium.org/gerrit/65886 Reviewed-by: Mike Frysinger Commit-Queue: Albert Chaulk Tested-by: Albert Chaulk --- crash_reporter/crash_sender | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 0422ca20f..723670126 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -84,6 +84,9 @@ TIMESTAMPS_DIR="/var/lib/crash_sender" # Temp directory for this process. TMP_DIR="" +# Chrome's crash report log file. +CHROME_CRASH_LOG="/var/log/chrome/Crash Reports/uploads.log" + lecho() { logger -t "${TAG}" "$@" } @@ -432,7 +435,20 @@ send_crash() { set -e if [ ${curl_result} -eq 0 ]; then - lecho "Crash report receipt ID $(cat ${report_id})" + local id="$(cat ${report_id})" + local product_name + local timestamp="$(date +%s)" + case ${product} in + Chrome_ChromeOS) + product_name="Chrome" + ;; + *) + product_name="ChromeOS" + ;; + esac + /usr/bin/printf '%s,%s\u00A0(%s)\n' \ + "${timestamp}" "${id}" "${product_name}" >> "${CHROME_CRASH_LOG}" + lecho "Crash report receipt ID ${id}" else lecho "Crash sending failed with: $(cat ${curl_stderr})" fi From 8a17bb928ae4f2cbb5530c53cb47da8d621929b9 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Sun, 15 Sep 2013 16:28:32 -0400 Subject: [PATCH 135/225] crash_sender: add support for command line options This lets us pass in custom env var settings w/out manipulating the environment. This makes execution a bit simpler in some cases. BUG=chromium:275910 TEST=`cbuildbot {arm,amd64,x86}-generic-full` works TEST=`crash_sender` on board works Change-Id: Icc32d7a179fee6a2e28d105d5790b2c55f12158b Reviewed-on: https://chromium-review.googlesource.com/169571 Reviewed-by: Ben Chan Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_sender | 47 ++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 723670126..458cb17c7 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -553,8 +553,53 @@ send_crashes() { done } +usage() { + cat <= Set env |var| to |val| (only some vars) +EOF + exit ${1:-1} +} + +parseargs() { + # Parse the command line arguments. + while [ $# -gt 0 ]; do + case $1 in + -e) + shift + case $1 in + FORCE_OFFICIAL=*|\ + MAX_CRASH_RATE=*|\ + MOCK_DEVELOPER_MODE=*|\ + OVERRIDE_PAUSE_SENDING=*|\ + SECONDS_SEND_SPREAD=*) + export "$1" + ;; + *) + lecho "Unknown var passed to -e: $1" + exit 1 + ;; + esac + ;; + -h) + usage 0 + ;; + *) + lecho "Uknown options: $*" + exit 1 + ;; + esac + shift + done +} + main() { trap cleanup EXIT INT TERM + + parseargs "$@" + if [ -e "${PAUSE_CRASH_SENDING}" ] && \ [ ${OVERRIDE_PAUSE_SENDING} -eq 0 ]; then lecho "Exiting early due to ${PAUSE_CRASH_SENDING}." @@ -593,5 +638,5 @@ if ! flock -n 9; then lecho "crash_sender is already running; quitting." exit 1 fi -main +main "$@" ) 9>"${CRASH_SENDER_LOCK}" From 55a515c31173a02035ae837d75666ac983352bf2 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Sun, 15 Sep 2013 14:31:17 -0400 Subject: [PATCH 136/225] crash_sender: use more random tmpdir The POSIX spec often requires 6 X's in temp paths. Change the code to use that rather than the non-standard 4. BUG=chromium:275910 TEST=`cbuildbot {arm,amd64,x86}-generic-full` works TEST=`crash_sender` on board works Change-Id: I97f5959afcb9f60dfd379d517904d5bbf5c502e3 Reviewed-on: https://chromium-review.googlesource.com/169680 Reviewed-by: Ben Chan Tested-by: Mike Frysinger Commit-Queue: Mike Frysinger --- crash_reporter/crash_sender | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 458cb17c7..a523d98d7 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -621,7 +621,7 @@ main() { fi done - TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXX)" + TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXXXX)" # Send system-wide crashes send_crashes "/var/spool/crash" From a569da0a2f624fd616b1afeae45e4a1c83d9bb8c Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 24 Sep 2013 14:15:36 -0400 Subject: [PATCH 137/225] build: respect CFLAGS when building warn_collector.l We update common.mk to the latest (not really needed, by might as well). Then we fix the lex building so it uses CFLAGS. This in turn triggers a clang warning in the file which we fix by adding proper printf attributes. BUG=chromium:297661 TEST=`VERBOSE=1 emerge-x86-alex crash-reporter` showed flags used w/lex Change-Id: I8db4b8b54b2ca748bb400ec6541287fa764f6d43 Reviewed-on: https://chromium-review.googlesource.com/170351 Reviewed-by: Yunlian Jiang Reviewed-by: Ben Chan Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/Makefile | 2 +- crash_reporter/common.mk | 104 +++++++++++++++++++++----------- crash_reporter/warn_collector.l | 1 + 3 files changed, 71 insertions(+), 36 deletions(-) diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile index 4f716fa58..546856061 100644 --- a/crash_reporter/Makefile +++ b/crash_reporter/Makefile @@ -58,7 +58,7 @@ all: CC_BINARY(warn_collector) lex.yy.c: $(SRC)/warn_collector.l flex $< clean: CLEAN(lex.yy.o) -$(eval $(call add_object_rules,lex.yy.o,CC,c)) +$(eval $(call add_object_rules,lex.yy.o,CC,c,CFLAGS)) # -- Unit Tests -- diff --git a/crash_reporter/common.mk b/crash_reporter/common.mk index 058e9063a..c44d91aea 100644 --- a/crash_reporter/common.mk +++ b/crash_reporter/common.mk @@ -51,9 +51,9 @@ # compiling the objects. This can be done by adding one of the following # to the Makefile: # - For C source files -# $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/gen_b.o,CC,c)) +# $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/b.o,CC,c,CFLAGS)) # - For C++ source files -# $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/gen_b.o,CXX,cc)) +# $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/b.o,CXX,cc,CXXFLAGS)) # # Exported targets meant to have prerequisites added to: # - all - Your desired targets should be given @@ -271,15 +271,48 @@ else CXXDRIVER = gcc endif +# Internal macro to support check_XXX macros below. +# Usage: $(call check_compile, [code], [compiler], [code_type], [c_flags], +# [extra_c_flags], [library_flags], [success_ret], [fail_ret]) +# Return: [success_ret] if compile succeeded, otherwise [fail_ret] +check_compile = $(shell printf '%b\n' $(1) | \ + $($(2)) $($(4)) -x $(3) $(LDFLAGS) $(5) - $(6) -o /dev/null > /dev/null 2>&1 \ + && echo "$(7)" || echo "$(8)") + +# Helper macro to check whether a test program will compile with the specified +# compiler flags. +# Usage: $(call check_compile_cc, [code], [flags], [alternate_flags]) +# Return: [flags] if compile succeeded, otherwise [alternate_flags] +check_compile_cc = $(call check_compile,$(1),CC,c,CFLAGS,$(2),,$(2),$(3)) +check_compile_cxx = $(call check_compile,$(1),CXX,c++,CXXFLAGS,$(2),,$(2),$(3)) + +# Helper macro to check whether a test program will compile with the specified +# libraries. +# Usage: $(call check_compile_cc, [code], [library_flags], [alternate_flags]) +# Return: [library_flags] if compile succeeded, otherwise [alternate_flags] +check_libs_cc = $(call check_compile,$(1),CC,c,CFLAGS,,$(2),$(2),$(3)) +check_libs_cxx = $(call check_compile,$(1),CXX,c++,CXXFLAGS,,$(2),$(2),$(3)) + +# Helper macro to check whether the compiler accepts the specified flags. +# Usage: $(call check_compile_cc, [flags], [alternate_flags]) +# Return: [flags] if compile succeeded, otherwise [alternate_flags] +check_cc = $(call check_compile_cc,'int main() { return 0; }',$(1),$(2)) +check_cxx = $(call check_compile_cxx,'int main() { return 0; }',$(1),$(2)) + +# Choose the stack protector flags based on whats supported by the compiler. +SSP_CFLAGS := $(call check_cc,-fstack-protector-strong) +ifeq ($(SSP_CFLAGS),) + SSP_CFLAGS := $(call check_cc,-fstack-protector-all) +endif + # To update these from an including Makefile: # CXXFLAGS += -mahflag # Append to the list # CXXFLAGS := -mahflag $(CXXFLAGS) # Prepend to the list # CXXFLAGS := $(filter-out badflag,$(CXXFLAGS)) # Filter out a value # The same goes for CFLAGS. -COMMON_CFLAGS-gcc := -fstack-protector-strong -fvisibility=internal -ggdb3 \ - -Wa,--noexecstack -COMMON_CFLAGS-clang := -fstack-protector-all -fvisibility=hidden -ggdb -COMMON_CFLAGS := -Wall -Werror -fno-strict-aliasing -O1 -Wformat=2 +COMMON_CFLAGS-gcc := -fvisibility=internal -ggdb3 -Wa,--noexecstack +COMMON_CFLAGS-clang := -fvisibility=hidden -ggdb +COMMON_CFLAGS := -Wall -Werror -fno-strict-aliasing $(SSP_CFLAGS) -O1 -Wformat=2 CXXFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CXXDRIVER)) CFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CDRIVER)) CPPFLAGS += -D_FORTIFY_SOURCE=2 @@ -650,23 +683,22 @@ all: # After the test have completed, if profiling, run coverage analysis tests: ifeq ($(MODE),profiling) - @$(ECHO) -n "COVERAGE gcov " - @$(ECHO) "[$(COLOR_YELLOW)STARTED$(COLOR_RESET)]" - $(QUIET)(FILES=""; \ - for GCNO in `find . -name "*.gcno"`; \ - do \ + @$(ECHO) "COVERAGE [$(COLOR_YELLOW)STARTED$(COLOR_RESET)]" + $(QUIET)FILES=""; \ + for GCNO in `find . -name "*.gcno"`; do \ GCDA="$${GCNO%.gcno}.gcda"; \ - [ -e $${GCDA} ] && FILES="$${FILES} $${GCDA}"; \ + if [ -e $${GCDA} ]; then \ + FILES="$${FILES} $${GCDA}"; \ + fi \ done; \ - gcov -l $${FILES}) - @$(ECHO) -n "COVERAGE gcov " - @$(ECHO) "[$(COLOR_YELLOW)FINISHED$(COLOR_RESET)]" - @$(ECHO) -n "COVERAGE lcov " - @$(ECHO) "[$(COLOR_YELLOW)STARTED$(COLOR_RESET)]" - $(QUIET)lcov --capture --directory . --output-file=lcov-coverage.info - $(QUIET)genhtml lcov-coverage.info --output-directory lcov-html - @$(ECHO) -n "COVERAGE lcov " - @$(ECHO) "[$(COLOR_YELLOW)FINISHED$(COLOR_RESET)]" + if [ -n "$${FILES}" ]; then \ + gcov -l $${FILES}; \ + lcov --capture --directory . \ + --output-file=lcov-coverage.info; \ + genhtml lcov-coverage.info \ + --output-directory lcov-html; \ + fi + @$(ECHO) "COVERAGE [$(COLOR_YELLOW)FINISHED$(COLOR_RESET)]" endif .PHONY: tests @@ -683,8 +715,10 @@ endif # TODO(wad) Move to -L $(SYSROOT) and fakechroot when qemu-user # doesn't hang traversing /proc from SYSROOT. +SUDO_CMD = sudo +UNSHARE_CMD = unshare QEMU_CMD = -ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),sudo , ) +ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),$(SUDO_CMD) , ) MOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) mount, \#) UMOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) umount, \#) QEMU_LDPATH = $(SYSROOT_LDPATH):/lib64:/lib:/usr/lib64:/usr/lib @@ -693,7 +727,7 @@ ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/lib:$(SYSROOT)/usr/lib64: ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/usr/lib ifeq ($(USE_QEMU),1) export QEMU_CMD = \ - sudo chroot $(SYSROOT) $(SYSROOT_OUT)qemu-$(QEMU_ARCH) \ + $(SUDO_CMD) chroot $(SYSROOT) $(SYSROOT_OUT)qemu-$(QEMU_ARCH) \ -drop-ld-preload \ -E LD_LIBRARY_PATH="$(QEMU_LDPATH):$(patsubst $(OUT),,$(LD_DIRS))" \ -E HOME="$(HOME)" -- @@ -725,22 +759,20 @@ define TEST_setup @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " @$(ECHO) "[$(COLOR_YELLOW)SETUP$(COLOR_RESET)]" $(QUIET)# Setup a target-specific results file + $(QUIET)(echo > $(OUT)$(TARGET_OR_MEMBER).setup.test) $(QUIET)(echo 1 > $(OUT)$(TARGET_OR_MEMBER).status.test) $(QUIET)(echo > $(OUT)$(TARGET_OR_MEMBER).cleanup.test) $(QUIET)# No setup if we are not using QEMU $(QUIET)# TODO(wad) this is racy until we use a vfs namespace $(call if_qemu,\ - $(QUIET)sudo mkdir -p "$(SYSROOT)/proc" "$(SYSROOT)/dev") + $(QUIET)(echo "mkdir -p '$(SYSROOT)/proc' '$(SYSROOT)/dev'" \ + >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) $(call if_qemu,\ - $(QUIET)$(MOUNT_CMD) --bind /proc "$(SYSROOT)/proc") + $(QUIET)(echo "$(MOUNT_CMD) --bind /proc '$(SYSROOT)/proc'" \ + >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) $(call if_qemu,\ - $(QUIET)$(MOUNT_CMD) --bind /dev "$(SYSROOT)/dev") - $(call if_qemu,\ - $(QUIET)(echo "$(UMOUNT_CMD) -l '$(SYSROOT)/proc'" \ - >> "$(OUT)$(TARGET_OR_MEMBER).cleanup.test")) - $(call if_qemu,\ - $(QUIET)(echo "$(UMOUNT_CMD) -l '$(SYSROOT)/dev'" \ - >> "$(OUT)$(TARGET_OR_MEMBER).cleanup.test")) + $(QUIET)(echo "$(MOUNT_CMD) --bind /dev '$(SYSROOT)/dev'" \ + >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) endef define TEST_teardown @@ -757,11 +789,13 @@ define TEST_run @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " @$(ECHO) "[$(COLOR_GREEN)RUN$(COLOR_RESET)]" $(QUIET)(echo 1 > "$(OUT)$(TARGET_OR_MEMBER).status.test") - -($(ROOT_CMD) $(QEMU_CMD) $(VALGRIND_CMD) \ + $(QUIET)(echo $(ROOT_CMD) $(QEMU_CMD) $(VALGRIND_CMD) \ "$(strip $(call if_qemu, $(SYSROOT_OUT),$(OUT))$(TARGET_OR_MEMBER))" \ $(if $(filter-out 0,$(words $(GTEST_ARGS.real))),$(GTEST_ARGS.real),\ - $(GTEST_ARGS)) && \ - echo 0 > "$(OUT)$(TARGET_OR_MEMBER).status.test") + $(GTEST_ARGS)) >> "$(OUT)$(TARGET_OR_MEMBER).setup.test") + -$(QUIET)$(call if_qemu,$(SUDO_CMD) $(UNSHARE_CMD) -m) $(SHELL) \ + $(OUT)$(TARGET_OR_MEMBER).setup.test \ + && echo 0 > "$(OUT)$(TARGET_OR_MEMBER).status.test" endef # Recursive list reversal so that we get RMDIR_ON_CLEAN in reverse order. diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l index 6fc2f5638..5cb12f12c 100644 --- a/crash_reporter/warn_collector.l +++ b/crash_reporter/warn_collector.l @@ -82,6 +82,7 @@ const char warn_dump_dir[] = "/var/run/kwarn"; const char *warn_dump_path = "/var/run/kwarn/warning"; const char *crash_reporter_command; +__attribute__((__format__(__printf__, 1, 2))) static void Die(const char *format, ...) { va_list ap; va_start(ap, format); From d9cd84277fb887924ecf355794537d884aca8424 Mon Sep 17 00:00:00 2001 From: Aaron Gable Date: Fri, 27 Sep 2013 18:35:54 -0700 Subject: [PATCH 138/225] Rewrite git/gerrit/gerrit-int urls to googlesource BUG=chromium:281665,chromium:290413 TEST=None Change-Id: I114bcb152e1bf1823ee49134e0f7f51719b21944 Reviewed-on: https://chromium-review.googlesource.com/171253 Reviewed-by: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/common.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/common.mk b/crash_reporter/common.mk index c44d91aea..a260aeb6b 100644 --- a/crash_reporter/common.mk +++ b/crash_reporter/common.mk @@ -7,7 +7,7 @@ # # NOTE NOTE NOTE # The authoritative common.mk is located in: -# http://git.chromium.org/gitweb/?p=chromiumos/platform/common-mk.git +# https://chromium.googlesource.com/chromiumos/platform/common-mk.git # Please make all changes there, then copy into place in other repos. # NOTE NOTE NOTE # From d2db5ff98889fbe0bb75bcf34c3ef31672bb38e4 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 8 Oct 2013 19:03:23 -0400 Subject: [PATCH 139/225] crash_reporter: clarify chrome crash ignore message We ignore Chrome crashes when it is the kernel that runs us. When Chrome itself runs us, we will take their crashes. The log output here though has long confused developers, so rephrase it slightly. BUG=None TEST=`cros_run_unit_tests -p crash-reporter --board x86-alex` passes TEST=`cbuildbot {amd64,x86}-generic-full` passes (VMTests) Change-Id: Ia13a766134f5a20a1cc4eaab74ac46633d3177eb Reviewed-on: https://chromium-review.googlesource.com/172261 Reviewed-by: Chris Masone Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/user_collector.cc | 3 ++- crash_reporter/user_collector_test.cc | 23 ++++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index eefe5dfca..014394aab 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -584,7 +584,8 @@ bool UserCollector::ShouldDump(bool has_owner_consent, // crashes towards user crashes, so user crashes really mean non-Chrome // user-space crashes. if (!handle_chrome_crashes && IsChromeExecName(exec)) { - *reason = "ignoring - chrome crash"; + *reason = "ignoring call by kernel - chrome crash; " + "waiting for chrome to call us directly"; return false; } diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index d1a3bcf04..71fc01531 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -20,6 +20,11 @@ static bool s_metrics = false; static const char kFilePath[] = "/my/path"; +// Keep in sync with UserCollector::ShouldDump. +static const char kChromeIgnoreMsg[] = + "ignoring call by kernel - chrome crash; " + "waiting for chrome to call us directly"; + using base::FilePath; using chromeos::FindLog; @@ -159,25 +164,25 @@ TEST_F(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage) { // When running a crash test, behave as normal. EXPECT_FALSE(collector_.ShouldDump(false, false, false, "chrome", &reason)); - EXPECT_EQ("ignoring - chrome crash", reason); + EXPECT_EQ(kChromeIgnoreMsg, reason); EXPECT_FALSE(collector_.ShouldDump(false, false, false, "supplied_Compositor", &reason)); - EXPECT_EQ("ignoring - chrome crash", reason); + EXPECT_EQ(kChromeIgnoreMsg, reason); EXPECT_FALSE(collector_.ShouldDump(false, false, false, "supplied_PipelineThread", &reason)); - EXPECT_EQ("ignoring - chrome crash", reason); + EXPECT_EQ(kChromeIgnoreMsg, reason); EXPECT_FALSE(collector_.ShouldDump(false, false, false, "Chrome_ChildIOThread", &reason)); - EXPECT_EQ("ignoring - chrome crash", reason); + EXPECT_EQ(kChromeIgnoreMsg, reason); EXPECT_FALSE(collector_.ShouldDump(false, false, false, "supplied_Chrome_ChildIOT", &reason)); - EXPECT_EQ("ignoring - chrome crash", reason); + EXPECT_EQ(kChromeIgnoreMsg, reason); EXPECT_FALSE(collector_.ShouldDump(false, false, false, "supplied_ChromotingClien", &reason)); - EXPECT_EQ("ignoring - chrome crash", reason); + EXPECT_EQ(kChromeIgnoreMsg, reason); EXPECT_FALSE(collector_.ShouldDump(false, false, false, "supplied_LocalInputMonit", &reason)); - EXPECT_EQ("ignoring - chrome crash", reason); + EXPECT_EQ(kChromeIgnoreMsg, reason); // When running a developer image, test that chrome crashes are handled // when the "handle_chrome_crashes" flag is set. @@ -243,7 +248,7 @@ TEST_F(UserCollectorTest, HandleChromeCrashWithConsent) { collector_.HandleCrash("5:2:ignored", "chrome"); EXPECT_TRUE(FindLog( "Received crash notification for chrome[5] sig 2")); - EXPECT_TRUE(FindLog("(ignoring - chrome crash)")); + EXPECT_TRUE(FindLog(kChromeIgnoreMsg)); ASSERT_EQ(s_crashes, 0); } @@ -252,7 +257,7 @@ TEST_F(UserCollectorTest, HandleSuppliedChromeCrashWithConsent) { collector_.HandleCrash("0:2:chrome", NULL); EXPECT_TRUE(FindLog( "Received crash notification for supplied_chrome[0] sig 2")); - EXPECT_TRUE(FindLog("(ignoring - chrome crash)")); + EXPECT_TRUE(FindLog(kChromeIgnoreMsg)); ASSERT_EQ(s_crashes, 0); } From 62b0c42baf898e06ca19660dbc902c58f6a57267 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Mon, 21 Oct 2013 19:08:47 -0700 Subject: [PATCH 140/225] crash-reporter: Remove unused constants reported by clang. BUG=chromium:309859 TEST=`FEATURES=test USE=clang emerge-lumpy crash-reporter` Change-Id: Ie6f78ad420322f44ce0c108cb23de299981f60a4 Reviewed-on: https://chromium-review.googlesource.com/173937 Tested-by: Ben Chan Reviewed-by: Yunlian Jiang Commit-Queue: Ben Chan --- crash_reporter/crash_collector_test.cc | 6 ------ crash_reporter/crash_reporter.cc | 2 -- 2 files changed, 8 deletions(-) diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 64a8653e1..661241edd 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -18,12 +18,6 @@ #include "gflags/gflags.h" #include "gtest/gtest.h" -// This test assumes the following standard binaries are installed. -static const char kBinBash[] = "/bin/bash"; -static const char kBinCp[] = "/bin/cp"; -static const char kBinEcho[] = "/bin/echo"; -static const char kBinFalse[] = "/bin/false"; - using base::FilePath; using chromeos::FindLog; using ::testing::Return; diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 134e8e4ed..f70e3b9b2 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -44,8 +44,6 @@ DEFINE_string(exe, "", "Executable name of crashing process"); static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; static const char kUserCrashSignal[] = "org.chromium.CrashReporter.UserCrash"; -static const char kUncleanShutdownFile[] = - "/var/lib/crash_reporter/pending_clean_shutdown"; static const char kKernelCrashDetected[] = "/var/run/kernel-crash-detected"; static const char kUncleanShutdownDetected[] = "/var/run/unclean-shutdown-detected"; From 130265e4bf5e053403d6281ca9e282d730bab672 Mon Sep 17 00:00:00 2001 From: Albert Chaulk Date: Tue, 22 Oct 2013 12:40:13 -0700 Subject: [PATCH 141/225] Copy in new common.mk BUG=chromium:304400 TEST=cros_run_unit_tests for daisy-drm, lumpy & amd64-corei7 Change-Id: I51a97881aace3cfd5b182f92bf3083a35ff56e72 Reviewed-on: https://chromium-review.googlesource.com/174053 Commit-Queue: Albert Chaulk Tested-by: Albert Chaulk Reviewed-by: Mike Frysinger --- crash_reporter/common.mk | 69 +++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/crash_reporter/common.mk b/crash_reporter/common.mk index a260aeb6b..50c1dd2b3 100644 --- a/crash_reporter/common.mk +++ b/crash_reporter/common.mk @@ -101,8 +101,6 @@ COLOR ?= 1 VERBOSE ?= 0 MODE ?= opt ARCH ?= $(shell uname -m) -NEEDS_ROOT = 0 -NEEDS_MOUNTS = 0 # Put objects in a separate tree based on makefile locations # This means you can build a tree without touching it: @@ -639,6 +637,11 @@ else USE_QEMU ?= 0 endif +# Normally we don't need to run as root or do bind mounts, so only +# enable it by default when we're using QEMU. +NEEDS_ROOT ?= $(USE_QEMU) +NEEDS_MOUNTS ?= $(USE_QEMU) + SYSROOT_OUT = $(OUT) ifneq ($(SYSROOT),) SYSROOT_OUT = $(subst $(SYSROOT),,$(OUT)) @@ -647,6 +650,15 @@ else SYSROOT = / endif +QEMU_NAME = qemu-$(QEMU_ARCH) +QEMU_PATH = /build/bin/$(QEMU_NAME) +QEMU_SYSROOT_PATH = $(SYSROOT)$(QEMU_PATH) +QEMU_SRC_PATH = /usr/bin/$(QEMU_NAME) +QEMU_BINFMT_PATH = /proc/sys/fs/binfmt_misc/$(QEMU_NAME) +QEMU_REGISTER_PATH = /proc/sys/fs/binfmt_misc/register + +QEMU_MAGIC_arm = ":$(QEMU_NAME):M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/build/bin/qemu-arm:" + # # Output full configuration at top level @@ -664,6 +676,9 @@ ifneq ($(MAKECMDGOALS),clean) $(info - COLOR=$(COLOR)) $(info - ARCH=$(ARCH)) $(info - QEMU_ARCH=$(QEMU_ARCH)) + $(info - USE_QEMU=$(USE_QEMU)) + $(info - NEEDS_ROOT=$(NEEDS_ROOT)) + $(info - NEEDS_MOUNTS=$(NEEDS_MOUNTS)) $(info - SYSROOT=$(SYSROOT)) $(info ) endif @@ -702,14 +717,39 @@ ifeq ($(MODE),profiling) endif .PHONY: tests -qemu_clean: - $(call if_qemu,$(call silent_rm,$(OUT)/qemu-$(QEMU_ARCH))) - qemu_chroot_install: ifeq ($(USE_QEMU),1) - $(QUIET)$(ECHO) "QEMU Preparing qemu-$(QEMU_ARCH)" - $(QUIET)cp -fu /usr/bin/qemu-$(QEMU_ARCH) $(OUT)/qemu-$(QEMU_ARCH) - $(QUIET)chmod a+rx $(OUT)/qemu-$(QEMU_ARCH) + $(QUIET)$(ECHO) "QEMU Preparing $(QEMU_NAME)" + @# Copying strategy + @# Compare /usr/bin/qemu inode to /build/$board/build/bin/qemu, if different + @# hard link to a temporary file, then rename temp to target. This should + @# ensure that once $QEMU_SYSROOT_PATH exists it will always exist, regardless + @# of simultaneous test setups. + $(QUIET)if [[ ! -e $(QEMU_SYSROOT_PATH) || \ + `stat -c %i $(QEMU_SRC_PATH)` != `stat -c %i $(QEMU_SYSROOT_PATH)` \ + ]]; then \ + $(ROOT_CMD) ln -Tf $(QEMU_SRC_PATH) $(QEMU_SYSROOT_PATH).$$$$; \ + $(ROOT_CMD) mv -Tf $(QEMU_SYSROOT_PATH).$$$$ $(QEMU_SYSROOT_PATH); \ + fi + + @# Prep the binfmt handler. First mount if needed, then unregister any bad + @# mappings and then register our mapping. + @# There may still be some race conditions here where one script de-registers + @# and another script starts executing before it gets re-registered, however + @# it should be rare. + -$(QUIET)[[ -e $(QEMU_REGISTER_PATH) ]] || \ + $(ROOT_CMD) mount binfmt_misc -t binfmt_misc \ + /proc/sys/fs/binfmt_misc + + -$(QUIET)if [[ -e $(QEMU_BINFMT_PATH) && \ + `awk '$$1 == "interpreter" {print $$NF}' $(QEMU_BINFMT_PATH)` != \ + "$(QEMU_PATH)" ]]; then \ + echo -1 | $(ROOT_CMD) tee $(QEMU_BINFMT_PATH) >/dev/null; \ + fi + + -$(if $(QEMU_MAGIC_$(ARCH)),$(QUIET)[[ -e $(QEMU_BINFMT_PATH) ]] || \ + echo $(QEMU_MAGIC_$(ARCH)) | $(ROOT_CMD) tee $(QEMU_REGISTER_PATH) \ + >/dev/null) endif .PHONY: qemu_clean qemu_chroot_install @@ -727,10 +767,10 @@ ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/lib:$(SYSROOT)/usr/lib64: ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/usr/lib ifeq ($(USE_QEMU),1) export QEMU_CMD = \ - $(SUDO_CMD) chroot $(SYSROOT) $(SYSROOT_OUT)qemu-$(QEMU_ARCH) \ + $(SUDO_CMD) chroot $(SYSROOT) $(QEMU_PATH) \ -drop-ld-preload \ -E LD_LIBRARY_PATH="$(QEMU_LDPATH):$(patsubst $(OUT),,$(LD_DIRS))" \ - -E HOME="$(HOME)" -- + -E HOME="$(HOME)" -E SRC="$(SRC)" -- # USE_QEMU conditional function define if_qemu $(1) @@ -765,7 +805,12 @@ define TEST_setup $(QUIET)# No setup if we are not using QEMU $(QUIET)# TODO(wad) this is racy until we use a vfs namespace $(call if_qemu,\ - $(QUIET)(echo "mkdir -p '$(SYSROOT)/proc' '$(SYSROOT)/dev'" \ + $(QUIET)(echo "mkdir -p '$(SYSROOT)/proc' '$(SYSROOT)/dev' \ + '$(SYSROOT)/mnt/host/source'" \ + >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) + $(call if_qemu,\ + $(QUIET)(echo "$(MOUNT_CMD) --bind /mnt/host/source \ + '$(SYSROOT)/mnt/host/source'" \ >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) $(call if_qemu,\ $(QUIET)(echo "$(MOUNT_CMD) --bind /proc '$(SYSROOT)/proc'" \ @@ -789,7 +834,7 @@ define TEST_run @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " @$(ECHO) "[$(COLOR_GREEN)RUN$(COLOR_RESET)]" $(QUIET)(echo 1 > "$(OUT)$(TARGET_OR_MEMBER).status.test") - $(QUIET)(echo $(ROOT_CMD) $(QEMU_CMD) $(VALGRIND_CMD) \ + $(QUIET)(echo $(ROOT_CMD) SRC="$(SRC)" $(QEMU_CMD) $(VALGRIND_CMD) \ "$(strip $(call if_qemu, $(SYSROOT_OUT),$(OUT))$(TARGET_OR_MEMBER))" \ $(if $(filter-out 0,$(words $(GTEST_ARGS.real))),$(GTEST_ARGS.real),\ $(GTEST_ARGS)) >> "$(OUT)$(TARGET_OR_MEMBER).setup.test") From 9f3bf8812058f8bc01f9a811475416e29ccfaca1 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Sun, 10 Nov 2013 16:33:21 -0500 Subject: [PATCH 142/225] warn_collector: fix building with newer flex Flex before 2.5.37 would call YY_INPUT with a result of type int*. But now it passes result as a yy_size_t* which leaders to errors: lex.yy.c:1419:3: error: passing 'yy_size_t *' (aka 'unsigned int *') to parameter of type 'int *' converts between pointers to integer types with different sign Change the type to match the newer flex. This means we'll fail when built with older versions, but we don't care about that. BUG=chromium:219621 TEST=`emerge-x86-alex crash-reporter` works w/flex-2.5.37 TEST=`emerge-stumpy crash-reporter` works w/flex-2.5.37 CQ-DEPEND=CL:176183 Change-Id: I64e7fdf43af50b7a8aa65125c6a83b9c77ea6ae6 Reviewed-on: https://chromium-review.googlesource.com/176248 Reviewed-by: Luigi Semenzato Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/warn_collector.l | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l index 5cb12f12c..0ca944a18 100644 --- a/crash_reporter/warn_collector.l +++ b/crash_reporter/warn_collector.l @@ -26,7 +26,7 @@ int WarnStart(void); void WarnEnd(void); -void WarnInput(char *buf, int *result, size_t max_size); +void WarnInput(char *buf, yy_size_t *result, size_t max_size); #define YY_INPUT(buf, result, max_size) WarnInput(buf, &result, max_size) @@ -174,11 +174,12 @@ static void WarnOpenInput(const char *path) { * * 3. We want to detect log rotation, and reopen the input file as needed. */ -void WarnInput(char *buf, int *result, size_t max_size) { +void WarnInput(char *buf, yy_size_t *result, size_t max_size) { while (1) { - *result = read(yyin_fd, buf, max_size); - if (*result < 0) + ssize_t ret = read(yyin_fd, buf, max_size); + if (ret < 0) Die("read: %s", strerror(errno)); + *result = ret; if (*result > 0 || fifo || filter) return; if (draining) { From f4c39b09f4f3559fc544fb6a7a47d6418b67ffad Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 6 Dec 2013 18:19:01 -0500 Subject: [PATCH 143/225] crash_sender: upload oldest crashes first The current logic is sorting crashes by date (which is good), except it sorts it such that the newest is first (which is bad). This means if a new process continues to crash, old crash reports will be perpetually stalled (it's a stack). Instead, let's start with the oldest first (like a fifo). BUG=chromium:326583 TEST=`cbuildbot x86-generic-full` passes VMTests Change-Id: Ifadedba80cb8c602c094c91d419e4d9645abda51 Reviewed-on: https://chromium-review.googlesource.com/179210 Reviewed-by: Ben Chan Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_sender | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index a523d98d7..1764ba832 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -490,7 +490,7 @@ send_crashes() { done # Look through all metadata (*.meta) files, if any exist. - for meta_path in $(ls -1t "${dir}"/*.meta 2>/dev/null); do + for meta_path in $(ls -1tr "${dir}"/*.meta 2>/dev/null); do lecho "Considering metadata ${meta_path}." local kind=$(get_kind "${meta_path}") From a59b3df60a1535b03f316684192dd1e81b0b76d7 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Mon, 16 Dec 2013 13:59:03 -0800 Subject: [PATCH 144/225] Append function name/offset to kernel warning signature. The current crash reporter signature for a kernel warning is the 8-digit hex hash for the warning. For convenience, add the function name + offset to the hash, when it is available. BUG=chromium:328948 TEST=ran unit test Change-Id: I8f047497c0556227a1dbdf650dabcd4c51aa8340 Reviewed-on: https://chromium-review.googlesource.com/180320 Tested-by: Luigi Semenzato Reviewed-by: Sameer Nanda Commit-Queue: Luigi Semenzato --- crash_reporter/warn_collector.l | 6 +++++- crash_reporter/warn_collector_test.sh | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l index 0ca944a18..6411b4d9b 100644 --- a/crash_reporter/warn_collector.l +++ b/crash_reporter/warn_collector.l @@ -124,6 +124,7 @@ static void SetHashSeen(uint32_t hash) { int WarnStart(void) { uint32_t hash; + char *spacep; if (filter) return 1; @@ -139,7 +140,10 @@ int WarnStart(void) { yyout = fopen(warn_dump_path, "w"); if (yyout == NULL) Die("fopen %s failed: %s\n", warn_dump_path, strerror(errno)); - fprintf(yyout, "%08x\n", hash); + spacep = index(yytext, ' '); + if (spacep == NULL || spacep[1] == '\0') + spacep = "unknown-function"; + fprintf(yyout, "%08x-%s\n", hash, spacep + 1); return 1; } diff --git a/crash_reporter/warn_collector_test.sh b/crash_reporter/warn_collector_test.sh index 0790181ba..d9bb6f945 100755 --- a/crash_reporter/warn_collector_test.sh +++ b/crash_reporter/warn_collector_test.sh @@ -38,7 +38,7 @@ check_log() { fail "expected ${n_expected} lines in ${TESTLOG}, found this instead: $(<"${TESTLOG}")" fi - if egrep -qv '^[0-9a-f]{8}$' "${TESTLOG}"; then + if egrep -qv '^[0-9a-f]{8}' "${TESTLOG}"; then fail "found bad lines in ${TESTLOG}: $(<"${TESTLOG}")" fi From 83ce73eac51b219dc1d112683149c486722e65c5 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 24 Jan 2014 01:30:05 -0500 Subject: [PATCH 145/225] remove unused gflags includes These files don't use gflags, so drop the includes. BUG=chromium:337753 TEST=`FEATURES=test emerge-x86-alex crash-reporter` passes Change-Id: I0a57e45cd16cd37b1361858e3313d59dced4eb5e Reviewed-on: https://chromium-review.googlesource.com/183644 Reviewed-by: Ben Chan Tested-by: Mike Frysinger Commit-Queue: Mike Frysinger --- crash_reporter/chrome_collector_test.cc | 1 - crash_reporter/crash_collector_test.cc | 1 - crash_reporter/kernel_collector_test.cc | 1 - crash_reporter/unclean_shutdown_collector_test.cc | 1 - crash_reporter/user_collector_test.cc | 1 - 5 files changed, 5 deletions(-) diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc index 9a5ee9bf5..e4d7904a9 100644 --- a/crash_reporter/chrome_collector_test.cc +++ b/crash_reporter/chrome_collector_test.cc @@ -12,7 +12,6 @@ #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/chrome_collector.h" -#include "gflags/gflags.h" #include "gtest/gtest.h" using base::FilePath; diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 661241edd..e98eb3798 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -15,7 +15,6 @@ #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/crash_collector.h" -#include "gflags/gflags.h" #include "gtest/gtest.h" using base::FilePath; diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 2dc50b403..a67ede653 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -10,7 +10,6 @@ #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/kernel_collector.h" -#include "gflags/gflags.h" #include "gtest/gtest.h" static int s_crashes = 0; diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc index 21e2aaa77..d56a0dc03 100644 --- a/crash_reporter/unclean_shutdown_collector_test.cc +++ b/crash_reporter/unclean_shutdown_collector_test.cc @@ -9,7 +9,6 @@ #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/unclean_shutdown_collector.h" -#include "gflags/gflags.h" #include "gtest/gtest.h" static int s_crashes = 0; diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 71fc01531..f00253c7b 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -12,7 +12,6 @@ #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/user_collector.h" -#include "gflags/gflags.h" #include "gtest/gtest.h" static int s_crashes = 0; From 8563d20e108c3801f107d640c9b161f1099e6beb Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Mon, 27 Jan 2014 19:30:13 -0800 Subject: [PATCH 146/225] Replace deprecated scoped_array with scoped_ptr BUG=chromium:338610 TEST=Build and run unit tests. Change-Id: I782cd5ddb0a112a51fca1972faba47b9ed2f7690 Reviewed-on: https://chromium-review.googlesource.com/184069 Reviewed-by: Mike Frysinger Commit-Queue: Ben Chan Tested-by: Ben Chan --- crash_reporter/crash_collector.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index d39d6f48a..f36401558 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -316,9 +316,9 @@ FilePath CrashCollector::GetProcessPath(pid_t pid) { } bool CrashCollector::GetSymlinkTarget(const FilePath &symlink, - FilePath *target) { + FilePath *target) { int max_size = 32; - scoped_array buffer; + scoped_ptr buffer; while (true) { buffer.reset(new char[max_size + 1]); ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size); From 1646eb87cbabc08b118bca06ee5e8445e33976d9 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 24 Jan 2014 01:30:54 -0500 Subject: [PATCH 147/225] add platform2 support BUG=chromium:337753 TEST=`FEATURES=test emerge-x86-alex platform2` builds & runs crash tests TEST=`cbuildbot chromiumos-sdk` passes Change-Id: Ia1f98b6058ff615ffc862955934096531c5d237d Reviewed-on: https://chromium-review.googlesource.com/183661 Reviewed-by: Mike Frysinger Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash-reporter.gyp | 168 ++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 crash_reporter/crash-reporter.gyp diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp new file mode 100644 index 000000000..d82282fed --- /dev/null +++ b/crash_reporter/crash-reporter.gyp @@ -0,0 +1,168 @@ +{ + # Shouldn't need this, but doesn't work otherwise. + # http://crbug.com/340086 + 'target_defaults': { + 'variables': { + 'deps': [ + ], + }, + }, + 'targets': [ + { + 'target_name': 'libcrash', + 'type': 'static_library', + 'variables': { + 'exported_deps': [ + 'glib-2.0', + 'gobject-2.0', + 'libchrome-<(libbase_ver)', + 'libpcrecpp', + ], + 'deps': ['<@(exported_deps)'], + }, + 'all_dependent_settings': { + 'variables': { + 'deps': [ + '<@(exported_deps)', + ], + }, + 'link_settings': { + 'libraries': [ + '-lgflags', + ], + }, + }, + 'dependencies': [ + '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', + ], + 'sources': [ + 'chrome_collector.cc', + 'crash_collector.cc', + 'kernel_collector.cc', + 'kernel_warning_collector.cc', + 'udev_collector.cc', + 'unclean_shutdown_collector.cc', + 'user_collector.cc', + ], + }, + { + 'target_name': 'crash_reporter', + 'type': 'executable', + 'variables': { + 'deps': [ + 'dbus-1', + 'dbus-glib-1', + ], + }, + 'dependencies': [ + 'libcrash', + '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', + '../metrics/metrics.gyp:libmetrics', + ], + 'sources': [ + 'crash_reporter.cc', + ], + }, + { + 'target_name': 'list_proxies', + 'type': 'executable', + 'variables': { + 'deps': [ + 'dbus-1', + 'dbus-glib-1', + 'libchrome-<(libbase_ver)', + ], + }, + 'link_settings': { + 'libraries': [ + '-lgflags', + ], + }, + 'dependencies': [ + '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', + ], + 'sources': [ + 'list_proxies.cc', + ], + }, + { + 'target_name': 'warn_collector', + 'type': 'executable', + 'variables': { + 'lexer_out_dir': 'crash-reporter', + }, + 'link_settings': { + 'libraries': [ + '-lfl', + ], + }, + 'dependencies': [ + '../metrics/metrics.gyp:libmetrics', + ], + 'sources': [ + 'warn_collector.l', + ], + 'includes': ['../common-mk/lex.gypi'], + }, + ], + 'conditions': [ + ['USE_test == 1', { + 'targets': [ + { + 'target_name': 'chrome_collector_test', + 'type': 'executable', + 'includes': ['../common-mk/common_test.gypi'], + 'dependencies': ['libcrash'], + 'sources': [ + 'chrome_collector_test.cc', + ] + }, + { + 'target_name': 'crash_collector_test', + 'type': 'executable', + 'includes': ['../common-mk/common_test.gypi'], + 'dependencies': ['libcrash'], + 'sources': [ + 'crash_collector_test.cc', + ] + }, + { + 'target_name': 'kernel_collector_test', + 'type': 'executable', + 'includes': ['../common-mk/common_test.gypi'], + 'dependencies': ['libcrash'], + 'sources': [ + 'kernel_collector_test.cc', + ] + }, + { + 'target_name': 'udev_collector_test', + 'type': 'executable', + 'includes': ['../common-mk/common_test.gypi'], + 'dependencies': ['libcrash'], + 'sources': [ + 'udev_collector_test.cc', + ] + }, + { + 'target_name': 'unclean_shutdown_collector_test', + 'type': 'executable', + 'includes': ['../common-mk/common_test.gypi'], + 'dependencies': ['libcrash'], + 'sources': [ + 'unclean_shutdown_collector_test.cc', + ] + }, + { + 'target_name': 'user_collector_test', + 'type': 'executable', + 'includes': ['../common-mk/common_test.gypi'], + 'dependencies': ['libcrash'], + 'sources': [ + 'user_collector_test.cc', + ] + }, + ], + }], + ], +} From 01ca40d6900891b0af8110dda132a625d1ff12c4 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 24 Jan 2014 16:46:02 -0500 Subject: [PATCH 148/225] convert list_proxies to base::CommandLine We need libbase already, so use that for command line processing rather than gflags. BUG=None TEST=`FEATURES=test emerge-x86-alex platform2` builds & runs crash tests TEST=`list_proxies --help` works TEST=ran other list_proxies commands and checked results Change-Id: I569fa88edb8fee4d6920965d644f69a0998a3202 Reviewed-on: https://chromium-review.googlesource.com/184627 Reviewed-by: Ben Chan Tested-by: Mike Frysinger Commit-Queue: Mike Frysinger --- crash_reporter/crash-reporter.gyp | 5 --- crash_reporter/list_proxies.cc | 65 ++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index d82282fed..8aca8cf60 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -73,11 +73,6 @@ 'libchrome-<(libbase_ver)', ], }, - 'link_settings': { - 'libraries': [ - '-lgflags', - ], - }, 'dependencies': [ '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', ], diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc index 9f3b3ece3..1be76e3e5 100644 --- a/crash_reporter/list_proxies.cc +++ b/crash_reporter/list_proxies.cc @@ -12,20 +12,12 @@ #include "base/command_line.h" #include "base/file_util.h" +#include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/strings/string_tokenizer.h" #include "base/values.h" #include "chromeos/dbus/dbus.h" #include "chromeos/syslog_logging.h" -#include "gflags/gflags.h" - -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -DEFINE_bool(quiet, false, "Only print the proxies"); -// Number of seconds to wait for browser to send us a signal -DEFINE_int32(timeout, 5, "Set timeout for browser resolving proxies"); -DEFINE_bool(verbose, false, "Print additional messages even " - "when not run from a TTY"); -#pragma GCC diagnostic error "-Wstrict-aliasing" const char kLibCrosProxyResolveSignalInterface[] = "org.chromium.CrashReporterLibcrosProxyResolvedInterface"; @@ -37,6 +29,26 @@ const char kLibCrosServiceResolveNetworkProxyMethodName[] = "ResolveNetworkProxy"; const char kNoProxy[] = "direct://"; +namespace switches { + +const unsigned kTimeoutDefault = 5; + +const char kHelp[] = "help"; +const char kQuiet[] = "quiet"; +const char kTimeout[] = "timeout"; +const char kVerbose[] = "verbose"; +// Help message to show when the --help command line switch is specified. +const char kHelpMessage[] = + "Chromium OS Crash helper: proxy lister\n" + "\n" + "Available Switches:\n" + " --quiet Only print the proxies\n" + " --verbose Print additional messages even when not run from a TTY\n" + " --timeout=N Set timeout for browser resolving proxies (default is 5)\n" + " --help Show this help.\n"; + +} // namespace switches + static const char *GetGErrorMessage(const GError *error) { if (!error) return "Unknown error."; @@ -138,7 +150,7 @@ static gboolean HandleBrowserTimeout(void *data) { return false; // only call once } -static bool ShowBrowserProxies(const char *url) { +static bool ShowBrowserProxies(std::string url, unsigned timeout) { GMainLoop *main_loop = g_main_loop_new(NULL, false); chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); @@ -168,7 +180,7 @@ static bool ShowBrowserProxies(const char *url) { if (!dbus_g_proxy_call(browser_proxy.gproxy(), kLibCrosServiceResolveNetworkProxyMethodName, &gerror, - G_TYPE_STRING, url, + G_TYPE_STRING, url.c_str(), G_TYPE_STRING, kLibCrosProxyResolveSignalInterface, G_TYPE_STRING, kLibCrosProxyResolveName, G_TYPE_INVALID, G_TYPE_INVALID)) { @@ -179,7 +191,7 @@ static bool ShowBrowserProxies(const char *url) { } // Setup a timeout in case the browser doesn't respond with our signal - g_timeout_add_seconds(FLAGS_timeout, &HandleBrowserTimeout, main_loop); + g_timeout_add_seconds(timeout, &HandleBrowserTimeout, main_loop); // Loop until we either get the proxy-resolved signal, or until the // timeout is reached. @@ -198,28 +210,45 @@ static bool ShowBrowserProxies(const char *url) { } int main(int argc, char *argv[]) { - google::ParseCommandLineFlags(&argc, &argv, true); CommandLine::Init(argc, argv); + CommandLine* cl = CommandLine::ForCurrentProcess(); + + if (cl->HasSwitch(switches::kHelp)) { + LOG(INFO) << switches::kHelpMessage; + return 0; + } + + bool quiet = cl->HasSwitch(switches::kQuiet); + bool verbose = cl->HasSwitch(switches::kVerbose); + + unsigned timeout = switches::kTimeoutDefault; + std::string str_timeout = cl->GetSwitchValueASCII(switches::kTimeout); + if (!str_timeout.empty() && !base::StringToUint(str_timeout, &timeout)) { + LOG(ERROR) << "Invalid timeout value: " << str_timeout; + return 1; + } // Default to logging to syslog. int init_flags = chromeos::kLogToSyslog; // Log to stderr if a TTY (and "-quiet" wasn't passed), or if "-verbose" // was passed. - if ((!FLAGS_quiet && isatty(STDERR_FILENO)) || FLAGS_verbose) + + if ((!quiet && isatty(STDERR_FILENO)) || verbose) init_flags |= chromeos::kLogToStderr; chromeos::InitLog(init_flags); ::g_type_init(); - const char *url = NULL; - if (argc >= 2) { - url = argv[1]; + std::string url; + CommandLine::StringVector urls = cl->GetArgs(); + if (!urls.empty()) { + url = urls[0]; LOG(INFO) << "Resolving proxies for URL: " << url; } else { LOG(INFO) << "Resolving proxies without URL"; } - if (!ShowBrowserProxies(url)) { + if (!ShowBrowserProxies(url, timeout)) { LOG(ERROR) << "Error resolving proxies via the browser"; LOG(INFO) << "Assuming direct proxy"; printf("%s\n", kNoProxy); From a557c1187ff19d422db2a9c951ecd8f7243e79bd Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 5 Feb 2014 22:55:39 -0500 Subject: [PATCH 149/225] update to libbase-242728 BUG=chromium:331128 TEST=`FEATURES=test emerge-x86-alex platform2` works TEST=`cbuildbot chromiumos-sdk` works CQ-DEPEND=CL:185131 Change-Id: Ia03a6ea7aaf6b4ee5d9c512ebf7080c0c28920f1 Reviewed-on: https://chromium-review.googlesource.com/185150 Reviewed-by: Ben Chan Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/chrome_collector.cc | 27 ++++++------ crash_reporter/chrome_collector.h | 2 +- crash_reporter/chrome_collector_test.cc | 7 ++-- crash_reporter/crash-reporter.gyp | 3 ++ crash_reporter/crash_collector.cc | 23 ++++++----- crash_reporter/crash_collector.h | 2 +- crash_reporter/crash_collector_test.cc | 31 +++++++------- crash_reporter/crash_reporter.cc | 14 +++---- crash_reporter/kernel_collector.cc | 13 +++--- crash_reporter/kernel_collector.h | 2 +- crash_reporter/kernel_collector_test.cc | 11 ++--- crash_reporter/kernel_warning_collector.cc | 11 +++-- crash_reporter/list_proxies.cc | 4 +- crash_reporter/udev_collector.cc | 6 +-- crash_reporter/udev_collector.h | 2 +- crash_reporter/udev_collector_test.cc | 6 +-- crash_reporter/unclean_shutdown_collector.cc | 10 ++--- crash_reporter/unclean_shutdown_collector.h | 2 +- .../unclean_shutdown_collector_test.cc | 22 +++++----- crash_reporter/user_collector.cc | 41 ++++++++++--------- crash_reporter/user_collector.h | 2 +- crash_reporter/user_collector_test.cc | 14 +++---- 22 files changed, 134 insertions(+), 121 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 55a7a5351..b5720975f 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -15,9 +15,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include "chromeos/process.h" #include "chromeos/syslog_logging.h" #include "chromeos/dbus/dbus.h" @@ -55,6 +55,9 @@ const char kRunning64[] = " running task "; const char kSymbolAndOffsetRE[] = "([[:alpha:]._$][[:alnum:]._$]*)\\+0x([[:xdigit:]]+)/0x([[:xdigit:]]+)"; +using base::FilePath; +using base::StringPrintf; + namespace { // Extract a string delimited by the given character, from the given offset @@ -108,7 +111,7 @@ bool GetDriErrorState(const chromeos::dbus::Proxy &proxy, if (written < 0 || (gsize)written != len) { LOG(ERROR) << "Could not write file " << error_state_path.value(); - file_util::Delete(error_state_path, false); + base::DeleteFile(error_state_path, false); return false; } @@ -312,7 +315,7 @@ bool WriteKernelTaskStates(const chromeos::dbus::Proxy &proxy, std::vector task_sates; if (!GetKernelTaskStates(proxy, &task_sates)) { - file_util::Delete(task_states_path, false); + base::DeleteFile(task_states_path, false); return false; } @@ -368,10 +371,10 @@ bool GetAdditionalLogs(const FilePath &log_path) { tar_process.AddArg(task_states_path.BaseName().value()); int res = tar_process.Run(); - file_util::Delete(error_state_path, false); - file_util::Delete(task_states_path, false); + base::DeleteFile(error_state_path, false); + base::DeleteFile(task_states_path, false); - if (res || !file_util::PathExists(log_path)) { + if (res || !base::PathExists(log_path)) { LOG(ERROR) << "Could not tar file " << log_path.value(); return false; } @@ -411,7 +414,7 @@ bool ChromeCollector::HandleCrash(const std::string &file_path, FilePath log_path = GetCrashPath(dir, dump_basename, "log.tar.xz"); std::string data; - if (!file_util::ReadFileToString(FilePath(file_path), &data)) { + if (!base::ReadFileToString(FilePath(file_path), &data)) { LOG(ERROR) << "Can't read crash log: " << file_path.c_str(); return false; } @@ -424,15 +427,15 @@ bool ChromeCollector::HandleCrash(const std::string &file_path, if (GetAdditionalLogs(log_path)) { int64 minidump_size = 0; int64 log_size = 0; - if (file_util::GetFileSize(minidump_path, &minidump_size) && - file_util::GetFileSize(log_path, &log_size) && + if (base::GetFileSize(minidump_path, &minidump_size) && + base::GetFileSize(log_path, &log_size) && minidump_size > 0 && log_size > 0 && minidump_size + log_size < kDefaultMaxUploadBytes) { AddCrashMetaData("log", log_path.value()); } else { LOG(INFO) << "Skipping logs upload to prevent discarding minidump " "because of report size limit < " << minidump_size + log_size; - file_util::Delete(log_path, false); + base::DeleteFile(log_path, false); } } diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h index 0a8a95671..ab594071f 100644 --- a/crash_reporter/chrome_collector.h +++ b/crash_reporter/chrome_collector.h @@ -7,7 +7,7 @@ #include -#include "base/file_path.h" +#include "base/files/file_path.h" #include "crash-reporter/crash_collector.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc index e4d7904a9..8a82cb210 100644 --- a/crash_reporter/chrome_collector_test.cc +++ b/crash_reporter/chrome_collector_test.cc @@ -8,7 +8,7 @@ #include "base/file_util.h" #include "base/files/scoped_temp_dir.h" -#include "base/string_split.h" +#include "base/strings/string_split.h" #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/chrome_collector.h" @@ -49,8 +49,7 @@ class ChromeCollectorTest : public ::testing::Test { void ExpectFileEquals(const char *golden, const char *file_path) { std::string contents; - EXPECT_TRUE(file_util::ReadFileToString(FilePath(file_path), - &contents)); + EXPECT_TRUE(base::ReadFileToString(FilePath(file_path), &contents)); EXPECT_EQ(golden, contents); } @@ -121,7 +120,7 @@ TEST_F(ChromeCollectorTest, File) { EXPECT_TRUE(meta.find("value3=ok") != std::string::npos); ExpectFileEquals("12345\n789\n12345", dir.Append("base-foo.txt.other").value().c_str()); - file_util::Delete(dir.Append("base-foo.txt.other"), false); + base::DeleteFile(dir.Append("base-foo.txt.other"), false); } int main(int argc, char **argv) { diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index 8aca8cf60..39835fa82 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -7,6 +7,9 @@ ], }, }, + 'variables': { + 'libbase_ver': 242728, + }, 'targets': [ { 'target_name': 'libcrash', diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index f36401558..0521a0645 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -22,9 +22,9 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" -#include "base/string_split.h" -#include "base/string_util.h" -#include "base/stringprintf.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "chromeos/cryptohome.h" #include "chromeos/dbus/dbus.h" #include "chromeos/dbus/service_constants.h" @@ -73,6 +73,7 @@ static const uid_t kRootGroup = 0; const int CrashCollector::kMaxCrashDirectorySize = 32; using base::FilePath; +using base::StringPrintf; CrashCollector::CrashCollector() : forced_crash_directory_(NULL), @@ -283,7 +284,7 @@ bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid, &directory_owner, &directory_group); - if (!file_util::PathExists(*crash_directory)) { + if (!base::PathExists(*crash_directory)) { // Create the spool directory with the appropriate mode (regardless of // umask) and ownership. mode_t old_mask = umask(0); @@ -297,7 +298,7 @@ bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid, umask(old_mask); } - if (!file_util::PathExists(*crash_directory)) { + if (!base::PathExists(*crash_directory)) { LOG(ERROR) << "Unable to create crash directory " << crash_directory->value().c_str(); return false; @@ -353,7 +354,7 @@ bool CrashCollector::GetExecutableBaseNameFromPid(pid_t pid, if (!GetSymlinkTarget(exe_path, &target)) { LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value() << " DirectoryExists: " - << file_util::DirectoryExists(process_path); + << base::DirectoryExists(process_path); // Try to further diagnose exe readlink failure cause. struct stat buf; int stat_result = stat(exe_path.value().c_str(), &buf); @@ -422,7 +423,7 @@ bool CrashCollector::ReadKeyValueFile( const char separator, std::map *dictionary) { std::string contents; - if (!file_util::ReadFileToString(path, &contents)) { + if (!base::ReadFileToString(path, &contents)) { return false; } typedef std::vector StringVector; @@ -507,7 +508,7 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, version = i->second; } int64 payload_size = -1; - file_util::GetFileSize(FilePath(payload_path), &payload_size); + base::GetFileSize(FilePath(payload_path), &payload_size); std::string meta_data = StringPrintf("%sexec_name=%s\n" "ver=%s\n" "payload=%s\n" @@ -527,7 +528,7 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, } bool CrashCollector::IsCrashTestInProgress() { - return file_util::PathExists(FilePath(kCrashTestInProgressPath)); + return base::PathExists(FilePath(kCrashTestInProgressPath)); } bool CrashCollector::IsDeveloperImage() { @@ -535,7 +536,7 @@ bool CrashCollector::IsDeveloperImage() { // for developer images. if (IsCrashTestInProgress()) return false; - return file_util::PathExists(FilePath(kLeaveCoreFile)); + return base::PathExists(FilePath(kLeaveCoreFile)); } bool CrashCollector::ShouldHandleChromeCrashes() { @@ -546,7 +547,7 @@ bool CrashCollector::ShouldHandleChromeCrashes() { // Check if there's an override to indicate we should indeed collect // chrome crashes. This allows the crashes to still be tracked when // they occur in autotests. See "crosbug.com/17987". - if (file_util::PathExists(FilePath(kCollectChromeFile))) + if (base::PathExists(FilePath(kCollectChromeFile))) return true; } // We default to ignoring chrome crashes. diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 19ec97b03..f1cf32e27 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -12,7 +12,7 @@ #include -#include "base/file_path.h" +#include "base/files/file_path.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST // User crash collector. diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index e98eb3798..7d3c1fe9f 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -10,14 +10,15 @@ #include #include "base/file_util.h" -#include "base/string_util.h" -#include "base/stringprintf.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/crash_collector.h" #include "gtest/gtest.h" using base::FilePath; +using base::StringPrintf; using chromeos::FindLog; using ::testing::Return; @@ -36,12 +37,12 @@ class CrashCollectorTest : public ::testing::Test { collector_.Initialize(CountCrash, IsMetrics); test_dir_ = FilePath("test"); - file_util::CreateDirectory(test_dir_); + base::CreateDirectory(test_dir_); chromeos::ClearLog(); } void TearDown() { - file_util::Delete(test_dir_, true); + base::DeleteFile(test_dir_, true); } bool CheckHasCapacity(); @@ -291,7 +292,7 @@ TEST_F(CrashCollectorTest, MetaData) { kPayload, strlen(kPayload))); collector_.AddCrashMetaData("foo", "bar"); collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value()); - EXPECT_TRUE(file_util::ReadFileToString(meta_file, &contents)); + EXPECT_TRUE(base::ReadFileToString(meta_file, &contents)); const char kExpectedMeta[] = "foo=bar\n" "exec_name=kernel\n" @@ -310,24 +311,24 @@ TEST_F(CrashCollectorTest, MetaData) { ASSERT_EQ(0, symlink(kMetaFileBasename, meta_symlink_path.value().c_str())); - ASSERT_TRUE(file_util::PathExists(meta_symlink_path)); + ASSERT_TRUE(base::PathExists(meta_symlink_path)); chromeos::ClearLog(); collector_.WriteCrashMetaData(meta_symlink_path, "kernel", payload_file.value()); // Target metadata contents should have stayed the same. contents.clear(); - EXPECT_TRUE(file_util::ReadFileToString(meta_file, &contents)); + EXPECT_TRUE(base::ReadFileToString(meta_file, &contents)); EXPECT_EQ(kExpectedMeta, contents); EXPECT_TRUE(FindLog("Unable to write")); // Test target of dangling symlink is not created. - file_util::Delete(meta_file, false); - ASSERT_FALSE(file_util::PathExists(meta_file)); + base::DeleteFile(meta_file, false); + ASSERT_FALSE(base::PathExists(meta_file)); chromeos::ClearLog(); collector_.WriteCrashMetaData(meta_symlink_path, "kernel", payload_file.value()); - EXPECT_FALSE(file_util::PathExists(meta_file)); + EXPECT_FALSE(base::PathExists(meta_file)); EXPECT_TRUE(FindLog("Unable to write")); } @@ -339,18 +340,18 @@ TEST_F(CrashCollectorTest, GetLogContents) { ASSERT_TRUE( file_util::WriteFile(config_file, kConfigContents, strlen(kConfigContents))); - file_util::Delete(FilePath(output_file), false); + base::DeleteFile(FilePath(output_file), false); EXPECT_FALSE(collector_.GetLogContents(config_file, "barfoo", output_file)); - EXPECT_FALSE(file_util::PathExists(output_file)); - file_util::Delete(FilePath(output_file), false); + EXPECT_FALSE(base::PathExists(output_file)); + base::DeleteFile(FilePath(output_file), false); EXPECT_TRUE(collector_.GetLogContents(config_file, "foobar", output_file)); - ASSERT_TRUE(file_util::PathExists(output_file)); + ASSERT_TRUE(base::PathExists(output_file)); std::string contents; - EXPECT_TRUE(file_util::ReadFileToString(output_file, &contents)); + EXPECT_TRUE(base::ReadFileToString(output_file, &contents)); EXPECT_EQ("hello world\n", contents); } diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index f70e3b9b2..1da1d8741 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -12,9 +12,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include "chromeos/syslog_logging.h" #include "crash-reporter/chrome_collector.h" #include "crash-reporter/kernel_collector.h" @@ -61,6 +61,7 @@ enum CrashKinds { static MetricsLibrary s_metrics_lib; using base::FilePath; +using base::StringPrintf; static bool IsFeedbackAllowed() { return s_metrics_lib.AreMetricsEnabled(); @@ -221,8 +222,8 @@ static int HandleKernelWarning(KernelWarningCollector static int GenerateKernelSignature(KernelCollector *kernel_collector) { std::string kcrash_contents; std::string signature; - if (!file_util::ReadFileToString(FilePath(FLAGS_generate_kernel_signature), - &kcrash_contents)) { + if (!base::ReadFileToString(FilePath(FLAGS_generate_kernel_signature), + &kcrash_contents)) { fprintf(stderr, "Could not read file.\n"); return 1; } @@ -259,8 +260,7 @@ static void OpenStandardFileDescriptors() { int main(int argc, char *argv[]) { OpenStandardFileDescriptors(); google::ParseCommandLineFlags(&argc, &argv, true); - FilePath my_path(argv[0]); - file_util::AbsolutePath(&my_path); + FilePath my_path = base::MakeAbsoluteFilePath(FilePath(argv[0])); s_metrics_lib.Init(); CommandLine::Init(argc, argv); chromeos::OpenLog(my_path.BaseName().value().c_str(), true); diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 579014001..17d150ed7 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -6,8 +6,8 @@ #include "base/file_util.h" #include "base/logging.h" -#include "base/string_util.h" -#include "base/stringprintf.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" static const char kDefaultKernelStackSignature[] = "kernel-UnspecifiedStackSignature"; @@ -46,6 +46,7 @@ static const char *s_pc_regex[] = { }; using base::FilePath; +using base::StringPrintf; COMPILE_ASSERT(arraysize(s_pc_regex) == KernelCollector::archCount, missing_arch_pc_regexp); @@ -79,7 +80,7 @@ bool KernelCollector::ReadRecordToString(std::string *contents, FilePath ramoops_record; GetRamoopsRecordPath(&ramoops_record, current_record); - if (!file_util::ReadFileToString(ramoops_record, &record)) { + if (!base::ReadFileToString(ramoops_record, &record)) { LOG(ERROR) << "Unable to open " << ramoops_record.value(); return false; } @@ -87,7 +88,7 @@ bool KernelCollector::ReadRecordToString(std::string *contents, if (record_re.FullMatch(record, &captured)){ // Found a match, append it to the content, and remove from pstore. contents->append(captured); - file_util::Delete(ramoops_record, false); + base::DeleteFile(ramoops_record, false); *record_found = true; } else { *record_found = false; @@ -115,7 +116,7 @@ bool KernelCollector::LoadParameters() { FilePath ramoops_record; GetRamoopsRecordPath(&ramoops_record, count); - if (!file_util::PathExists(ramoops_record)) + if (!base::PathExists(ramoops_record)) break; } @@ -234,7 +235,7 @@ bool KernelCollector::Enable() { else { FilePath ramoops_record; GetRamoopsRecordPath(&ramoops_record, 0); - if (!file_util::PathExists(ramoops_record)) { + if (!base::PathExists(ramoops_record)) { LOG(WARNING) << "Kernel does not support crash dumping"; return false; } diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index 17b55a0fe..c965cd39d 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -9,7 +9,7 @@ #include -#include "base/file_path.h" +#include "base/files/file_path.h" #include "crash-reporter/crash_collector.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index a67ede653..dd6e5ba62 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -5,8 +5,8 @@ #include #include "base/file_util.h" -#include "base/string_util.h" -#include "base/stringprintf.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/kernel_collector.h" @@ -19,6 +19,7 @@ static const char kTestKCrash[] = "test/kcrash"; static const char kTestCrashDirectory[] = "test/crash_directory"; using base::FilePath; +using base::StringPrintf; using chromeos::FindLog; using chromeos::GetLog; @@ -69,7 +70,7 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignatureBase) { } TEST_F(KernelCollectorTest, LoadPreservedDump) { - ASSERT_FALSE(file_util::PathExists(test_kcrash_)); + ASSERT_FALSE(base::PathExists(test_kcrash_)); std::string dump; dump.clear(); @@ -272,9 +273,9 @@ TEST_F(KernelCollectorTest, CollectOK) { ASSERT_NE(std::string::npos, end_pos); filename = filename.substr(0, end_pos); ASSERT_EQ(0, filename.find(kTestCrashDirectory)); - ASSERT_TRUE(file_util::PathExists(FilePath(filename))); + ASSERT_TRUE(base::PathExists(FilePath(filename))); std::string contents; - ASSERT_TRUE(file_util::ReadFileToString(FilePath(filename), &contents)); + ASSERT_TRUE(base::ReadFileToString(FilePath(filename), &contents)); ASSERT_EQ("something", contents); } diff --git a/crash_reporter/kernel_warning_collector.cc b/crash_reporter/kernel_warning_collector.cc index b504f497b..f8188c7fb 100644 --- a/crash_reporter/kernel_warning_collector.cc +++ b/crash_reporter/kernel_warning_collector.cc @@ -6,9 +6,9 @@ #include "base/file_util.h" #include "base/logging.h" -#include "base/string_number_conversions.h" -#include "base/string_util.h" -#include "base/stringprintf.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" namespace { const char kExecName[] = "kernel-warning"; @@ -18,6 +18,9 @@ const pid_t kKernelPid = 0; const uid_t kRootUid = 0; } // namespace +using base::FilePath; +using base::StringPrintf; + KernelWarningCollector::KernelWarningCollector() { } @@ -27,7 +30,7 @@ KernelWarningCollector::~KernelWarningCollector() { bool KernelWarningCollector::LoadKernelWarning(std::string *content, std::string *hash_string) { FilePath kernel_warning_path(kKernelWarningPath); - if (!file_util::ReadFileToString(kernel_warning_path, content)) { + if (!base::ReadFileToString(kernel_warning_path, content)) { LOG(ERROR) << "Could not open " << kKernelWarningPath; return false; } diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc index 1be76e3e5..1f9db763c 100644 --- a/crash_reporter/list_proxies.cc +++ b/crash_reporter/list_proxies.cc @@ -12,8 +12,8 @@ #include "base/command_line.h" #include "base/file_util.h" -#include "base/string_number_conversions.h" -#include "base/string_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "base/strings/string_tokenizer.h" #include "base/values.h" #include "chromeos/dbus/dbus.h" diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc index 4e4b21340..504f90e11 100644 --- a/crash_reporter/udev_collector.cc +++ b/crash_reporter/udev_collector.cc @@ -7,8 +7,8 @@ #include "base/basictypes.h" #include "base/file_util.h" #include "base/logging.h" -#include "base/string_split.h" -#include "base/string_util.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" #include "chromeos/process.h" static const char kCollectUdevSignature[] = "crash_reporter-udev-collection"; @@ -77,7 +77,7 @@ bool UdevCollector::HandleCrash(const std::string &udev_event) { int process_result = gzip_process.Run(); FilePath crash_path_zipped = FilePath(crash_path.value() + ".gz"); // If the zip file was not created, use the uncompressed file. - if (process_result != 0 || !file_util::PathExists(crash_path_zipped)) + if (process_result != 0 || !base::PathExists(crash_path_zipped)) LOG(ERROR) << "Could not create zip file " << crash_path_zipped.value(); else crash_path = crash_path_zipped; diff --git a/crash_reporter/udev_collector.h b/crash_reporter/udev_collector.h index 29c19ef78..0976d3e32 100644 --- a/crash_reporter/udev_collector.h +++ b/crash_reporter/udev_collector.h @@ -7,7 +7,7 @@ #include -#include "base/file_path.h" +#include "base/files/file_path.h" #include "crash-reporter/crash_collector.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc index 31fdbf4f7..6a5c8569e 100644 --- a/crash_reporter/udev_collector_test.cc +++ b/crash_reporter/udev_collector_test.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/file_util.h" +#include "base/files/file_enumerator.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/scoped_ptr.h" #include "chromeos/test_helpers.h" @@ -32,9 +33,8 @@ bool IsMetrics() { // Returns the number of compressed crash log files found in the given path. int GetNumLogFiles(const FilePath& path) { - file_util::FileEnumerator enumerator(path, false, - file_util::FileEnumerator::FILES, - "*.log.gz"); + base::FileEnumerator enumerator(path, false, base::FileEnumerator::FILES, + "*.log.gz"); int num_files = 0; for (FilePath file_path = enumerator.Next(); !file_path.value().empty(); diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc index 14cc8f415..ae0775cb5 100644 --- a/crash_reporter/unclean_shutdown_collector.cc +++ b/crash_reporter/unclean_shutdown_collector.cc @@ -28,7 +28,7 @@ UncleanShutdownCollector::~UncleanShutdownCollector() { bool UncleanShutdownCollector::Enable() { FilePath file_path(unclean_shutdown_file_); - file_util::CreateDirectory(file_path.DirName()); + base::CreateDirectory(file_path.DirName()); if (file_util::WriteFile(file_path, "", 0) != 0) { LOG(ERROR) << "Unable to create shutdown check file"; return false; @@ -37,19 +37,19 @@ bool UncleanShutdownCollector::Enable() { } bool UncleanShutdownCollector::DeleteUncleanShutdownFiles() { - if (!file_util::Delete(FilePath(unclean_shutdown_file_), false)) { + if (!base::DeleteFile(FilePath(unclean_shutdown_file_), false)) { LOG(ERROR) << "Failed to delete unclean shutdown file " << unclean_shutdown_file_; return false; } // Delete power manager state file if it exists. - file_util::Delete(powerd_suspended_file_, false); + base::DeleteFile(powerd_suspended_file_, false); return true; } bool UncleanShutdownCollector::Collect() { FilePath unclean_file_path(unclean_shutdown_file_); - if (!file_util::PathExists(unclean_file_path)) { + if (!base::PathExists(unclean_file_path)) { return false; } LOG(WARNING) << "Last shutdown was not clean"; @@ -72,7 +72,7 @@ bool UncleanShutdownCollector::Disable() { bool UncleanShutdownCollector::DeadBatteryCausedUncleanShutdown() { // Check for case of battery running out while suspended. - if (file_util::PathExists(powerd_suspended_file_)) { + if (base::PathExists(powerd_suspended_file_)) { LOG(INFO) << "Unclean shutdown occurred while suspended. Not counting " << "toward unclean shutdown statistic."; return true; diff --git a/crash_reporter/unclean_shutdown_collector.h b/crash_reporter/unclean_shutdown_collector.h index 7a5b669e2..ec21b7cf8 100644 --- a/crash_reporter/unclean_shutdown_collector.h +++ b/crash_reporter/unclean_shutdown_collector.h @@ -7,7 +7,7 @@ #include -#include "base/file_path.h" +#include "base/files/file_path.h" #include "crash-reporter/crash_collector.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc index d56a0dc03..f9ad05466 100644 --- a/crash_reporter/unclean_shutdown_collector_test.cc +++ b/crash_reporter/unclean_shutdown_collector_test.cc @@ -5,7 +5,7 @@ #include #include "base/file_util.h" -#include "base/string_util.h" +#include "base/strings/string_util.h" #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/unclean_shutdown_collector.h" @@ -37,7 +37,7 @@ class UncleanShutdownCollectorTest : public ::testing::Test { rmdir(kTestDirectory); test_unclean_ = FilePath(kTestUnclean); collector_.unclean_shutdown_file_ = kTestUnclean; - file_util::Delete(test_unclean_, true); + base::DeleteFile(test_unclean_, true); // Set up an alternate power manager state file as well collector_.powerd_suspended_file_ = FilePath(kTestSuspended); chromeos::ClearLog(); @@ -55,13 +55,13 @@ class UncleanShutdownCollectorTest : public ::testing::Test { TEST_F(UncleanShutdownCollectorTest, EnableWithoutParent) { ASSERT_TRUE(collector_.Enable()); - ASSERT_TRUE(file_util::PathExists(test_unclean_)); + ASSERT_TRUE(base::PathExists(test_unclean_)); } TEST_F(UncleanShutdownCollectorTest, EnableWithParent) { mkdir(kTestDirectory, 0777); ASSERT_TRUE(collector_.Enable()); - ASSERT_TRUE(file_util::PathExists(test_unclean_)); + ASSERT_TRUE(base::PathExists(test_unclean_)); } TEST_F(UncleanShutdownCollectorTest, EnableCannotWrite) { @@ -72,9 +72,9 @@ TEST_F(UncleanShutdownCollectorTest, EnableCannotWrite) { TEST_F(UncleanShutdownCollectorTest, CollectTrue) { ASSERT_TRUE(collector_.Enable()); - ASSERT_TRUE(file_util::PathExists(test_unclean_)); + ASSERT_TRUE(base::PathExists(test_unclean_)); ASSERT_TRUE(collector_.Collect()); - ASSERT_FALSE(file_util::PathExists(test_unclean_)); + ASSERT_FALSE(base::PathExists(test_unclean_)); ASSERT_EQ(1, s_crashes); ASSERT_TRUE(FindLog("Last shutdown was not clean")); } @@ -86,20 +86,20 @@ TEST_F(UncleanShutdownCollectorTest, CollectFalse) { TEST_F(UncleanShutdownCollectorTest, CollectDeadBatterySuspended) { ASSERT_TRUE(collector_.Enable()); - ASSERT_TRUE(file_util::PathExists(test_unclean_)); + ASSERT_TRUE(base::PathExists(test_unclean_)); file_util::WriteFile(collector_.powerd_suspended_file_, "", 0); ASSERT_FALSE(collector_.Collect()); - ASSERT_FALSE(file_util::PathExists(test_unclean_)); - ASSERT_FALSE(file_util::PathExists(collector_.powerd_suspended_file_)); + ASSERT_FALSE(base::PathExists(test_unclean_)); + ASSERT_FALSE(base::PathExists(collector_.powerd_suspended_file_)); ASSERT_EQ(0, s_crashes); ASSERT_TRUE(FindLog("Unclean shutdown occurred while suspended.")); } TEST_F(UncleanShutdownCollectorTest, Disable) { ASSERT_TRUE(collector_.Enable()); - ASSERT_TRUE(file_util::PathExists(test_unclean_)); + ASSERT_TRUE(base::PathExists(test_unclean_)); ASSERT_TRUE(collector_.Disable()); - ASSERT_FALSE(file_util::PathExists(test_unclean_)); + ASSERT_FALSE(base::PathExists(test_unclean_)); ASSERT_FALSE(collector_.Collect()); } diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 014394aab..4afad4356 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -19,9 +19,9 @@ #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "base/stl_util.h" -#include "base/string_split.h" -#include "base/string_util.h" -#include "base/stringprintf.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "chromeos/process.h" #include "chromeos/syslog_logging.h" #include "gflags/gflags.h" @@ -54,6 +54,7 @@ const char *UserCollector::kUserId = "Uid:\t"; const char *UserCollector::kGroupId = "Gid:\t"; using base::FilePath; +using base::StringPrintf; UserCollector::UserCollector() : generate_diagnostics_(false), @@ -200,9 +201,9 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid, // the error log. We cannot just append to files because we need // to always create new files to prevent attack. std::string diag_log_contents; - file_util::ReadFileToString(diag_log_path, &diag_log_contents); + base::ReadFileToString(diag_log_path, &diag_log_contents); error_log.append(diag_log_contents); - file_util::Delete(diag_log_path, false); + base::DeleteFile(diag_log_path, false); } FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); @@ -217,12 +218,12 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid, bool UserCollector::CopyOffProcFiles(pid_t pid, const FilePath &container_dir) { - if (!file_util::CreateDirectory(container_dir)) { + if (!base::CreateDirectory(container_dir)) { PLOG(ERROR) << "Could not create " << container_dir.value().c_str(); return false; } FilePath process_path = GetProcessPath(pid); - if (!file_util::PathExists(process_path)) { + if (!base::PathExists(process_path)) { LOG(ERROR) << "Path " << process_path.value() << " does not exist"; return false; } @@ -234,8 +235,8 @@ bool UserCollector::CopyOffProcFiles(pid_t pid, "status" }; for (unsigned i = 0; i < arraysize(proc_files); ++i) { - if (!file_util::CopyFile(process_path.Append(proc_files[i]), - container_dir.Append(proc_files[i]))) { + if (!base::CopyFile(process_path.Append(proc_files[i]), + container_dir.Append(proc_files[i]))) { LOG(ERROR) << "Could not copy " << proc_files[i] << " file"; return false; } @@ -247,7 +248,7 @@ bool UserCollector::ValidateProcFiles(const FilePath &container_dir) const { // Check if the maps file is empty, which could be due to the crashed // process being reaped by the kernel before finishing a core dump. int64 file_size = 0; - if (!file_util::GetFileSize(container_dir.Append("maps"), &file_size)) { + if (!base::GetFileSize(container_dir.Append("maps"), &file_size)) { LOG(ERROR) << "Could not get the size of maps file"; return false; } @@ -267,7 +268,7 @@ UserCollector::ErrorType UserCollector::ValidateCoreFile( } char e_ident[EI_NIDENT]; - bool read_ok = file_util::ReadFromFD(fd, e_ident, sizeof(e_ident)); + bool read_ok = base::ReadFromFD(fd, e_ident, sizeof(e_ident)); HANDLE_EINTR(close(fd)); if (!read_ok) { LOG(ERROR) << "Could not read header of core file"; @@ -304,7 +305,7 @@ bool UserCollector::GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid, } uid_t uid; - if (file_util::ReadFileToString(process_path.Append("status"), &status)) { + if (base::ReadFileToString(process_path.Append("status"), &status)) { std::vector status_lines; base::SplitString(status, '\n', &status_lines); @@ -330,7 +331,7 @@ bool UserCollector::GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid, } else { LOG(ERROR) << "Could not read status file and kernel did not supply UID"; LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: " - << file_util::DirectoryExists(process_path); + << base::DirectoryExists(process_path); return false; } @@ -344,13 +345,13 @@ bool UserCollector::GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid, bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { // Copy off all stdin to a core file. FilePath stdin_path("/dev/fd/0"); - if (file_util::CopyFile(stdin_path, core_path)) { + if (base::CopyFile(stdin_path, core_path)) { return true; } PLOG(ERROR) << "Could not write core file"; // If the file system was full, make sure we remove any remnants. - file_util::Delete(core_path, false); + base::DeleteFile(core_path, false); return false; } @@ -375,14 +376,14 @@ bool UserCollector::RunCoreToMinidump(const FilePath &core_path, int errorlevel = core2md.Run(); std::string output; - file_util::ReadFileToString(output_path, &output); + base::ReadFileToString(output_path, &output); if (errorlevel != 0) { LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath << " [result=" << errorlevel << "]: " << output; return false; } - if (!file_util::PathExists(minidump_path)) { + if (!base::PathExists(minidump_path)) { LOG(ERROR) << "Minidump file " << minidump_path.value() << " was not created"; return false; @@ -443,7 +444,7 @@ UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash( // Delete a pre-existing directory from crash reporter that may have // been left around for diagnostics from a failed conversion attempt. // If we don't, existing files can cause forking to fail. - file_util::Delete(container_dir, true); + base::DeleteFile(container_dir, true); std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); FilePath core_path = GetCrashPath(crash_path, dump_basename, "core"); FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); @@ -469,13 +470,13 @@ UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash( minidump_path.value()); if (!IsDeveloperImage()) { - file_util::Delete(core_path, false); + base::DeleteFile(core_path, false); } else { LOG(INFO) << "Leaving core file at " << core_path.value() << " due to developer image"; } - file_util::Delete(container_dir, true); + base::DeleteFile(container_dir, true); return kErrorNone; } diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 237a99003..6c82fa8dd 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -8,7 +8,7 @@ #include #include -#include "base/file_path.h" +#include "base/files/file_path.h" #include "crash-reporter/crash_collector.h" #include "gtest/gtest_prod.h" // for FRIEND_TEST diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index f00253c7b..d416a7bbd 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -8,7 +8,7 @@ #include "base/file_util.h" #include "base/files/scoped_temp_dir.h" -#include "base/string_split.h" +#include "base/strings/string_split.h" #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/user_collector.h" @@ -42,7 +42,7 @@ class UserCollectorTest : public ::testing::Test { kFilePath, IsMetrics, false); - file_util::Delete(FilePath("test"), true); + base::DeleteFile(FilePath("test"), true); mkdir("test", 0777); collector_.set_core_pattern_file("test/core_pattern"); collector_.set_core_pipe_limit_file("test/core_pipe_limit"); @@ -54,7 +54,7 @@ class UserCollectorTest : public ::testing::Test { void ExpectFileEquals(const char *golden, const char *file_path) { std::string contents; - EXPECT_TRUE(file_util::ReadFileToString(FilePath(file_path), + EXPECT_TRUE(base::ReadFileToString(FilePath(file_path), &contents)); EXPECT_EQ(golden, contents); } @@ -91,7 +91,7 @@ TEST_F(UserCollectorTest, EnableNoPipeLimitFileAccess) { ASSERT_EQ(s_crashes, 0); // Core pattern should not be written if we cannot access the pipe limit // or otherwise we may set a pattern that results in infinite recursion. - ASSERT_FALSE(file_util::PathExists(FilePath("test/core_pattern"))); + ASSERT_FALSE(base::PathExists(FilePath("test/core_pattern"))); EXPECT_TRUE(FindLog("Enabling user crash handling")); EXPECT_TRUE(FindLog("Unable to write /does_not_exist")); } @@ -463,7 +463,7 @@ TEST_F(UserCollectorTest, CopyOffProcFilesOK) { }; for (unsigned i = 0; i < sizeof(expectations)/sizeof(expectations[0]); ++i) { EXPECT_EQ(expectations[i].exists, - file_util::PathExists( + base::PathExists( container_path.Append(expectations[i].name))); } } @@ -479,13 +479,13 @@ TEST_F(UserCollectorTest, ValidateProcFiles) { // maps file is empty FilePath maps_file = container_dir.Append("maps"); ASSERT_EQ(0, file_util::WriteFile(maps_file, NULL, 0)); - ASSERT_TRUE(file_util::PathExists(maps_file)); + ASSERT_TRUE(base::PathExists(maps_file)); EXPECT_FALSE(collector_.ValidateProcFiles(container_dir)); // maps file is not empty const char data[] = "test data"; ASSERT_EQ(sizeof(data), file_util::WriteFile(maps_file, data, sizeof(data))); - ASSERT_TRUE(file_util::PathExists(maps_file)); + ASSERT_TRUE(base::PathExists(maps_file)); EXPECT_TRUE(collector_.ValidateProcFiles(container_dir)); } From f1a557d198fedf88412c775311d86770b1b7b15d Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 5 Feb 2014 21:05:10 -0500 Subject: [PATCH 150/225] drop unused makefiles We use platform2 now w/gyp, so don't need these anymore. BUG=chromium:337753 TEST=`FEATURES=test emerge-x86-alex platform2` still passes Change-Id: I677b646dd56342be5cd98cb57852cc62c805240b Reviewed-on: https://chromium-review.googlesource.com/185561 Reviewed-by: Ben Chan Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/Makefile | 94 ---- crash_reporter/common.mk | 924 ----------------------------------- crash_reporter/make_tests.sh | 16 - 3 files changed, 1034 deletions(-) delete mode 100644 crash_reporter/Makefile delete mode 100644 crash_reporter/common.mk delete mode 100755 crash_reporter/make_tests.sh diff --git a/crash_reporter/Makefile b/crash_reporter/Makefile deleted file mode 100644 index 546856061..000000000 --- a/crash_reporter/Makefile +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# TODO(mkrebs): This currently grabs a copy we made from -# src/platform/common-mk; we should instead share it. -include common.mk - -BASE_VER ?= 125070 -PKG_CONFIG ?= pkg-config - -CRASH_OBJS = \ - chrome_collector.o \ - crash_collector.o \ - kernel_collector.o \ - kernel_warning_collector.o \ - udev_collector.o \ - unclean_shutdown_collector.o \ - user_collector.o - -PC_DEPS = glib-2.0 gobject-2.0 libpcrecpp libchrome-$(BASE_VER) \ - libchromeos-$(BASE_VER) -PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) -PC_LIBS := $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) - -DBUS_PC_DEPS = dbus-1 dbus-glib-1 -DBUS_PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(DBUS_PC_DEPS)) -DBUS_PC_LIBS := $(shell $(PKG_CONFIG) --libs $(DBUS_PC_DEPS)) - -CPPFLAGS += -I$(SRC)/.. -I$(SYSROOT)/usr/include/google-breakpad $(PC_CFLAGS) -LDLIBS = -lgflags $(PC_LIBS) - - -# -- Executable crash_reporter -- - -CXX_BINARY(crash_reporter): CPPFLAGS += $(DBUS_PC_CFLAGS) -CXX_BINARY(crash_reporter): LDLIBS += -lmetrics $(DBUS_PC_LIBS) -CXX_BINARY(crash_reporter): crash_reporter.o $(CRASH_OBJS) -clean: CLEAN(crash_reporter) -all: CXX_BINARY(crash_reporter) - - -# -- Executable list_proxies -- - -CXX_BINARY(list_proxies): CPPFLAGS += $(DBUS_PC_CFLAGS) -CXX_BINARY(list_proxies): LDLIBS += $(DBUS_PC_LIBS) -CXX_BINARY(list_proxies): list_proxies.o -clean: CLEAN(list_proxies) -all: CXX_BINARY(list_proxies) - - -# -- Executable warn_collector -- - -CC_BINARY(warn_collector): LDLIBS += -lfl -lmetrics -CC_BINARY(warn_collector): lex.yy.o -clean: CLEAN(warn_collector) -all: CC_BINARY(warn_collector) -lex.yy.c: $(SRC)/warn_collector.l - flex $< -clean: CLEAN(lex.yy.o) -$(eval $(call add_object_rules,lex.yy.o,CC,c,CFLAGS)) - - -# -- Unit Tests -- - -UNITTEST_LIBS := $(shell gmock-config --libs) $(shell gtest-config --libs) - -# Uncomment these to just run specific test(s). -#TEST_BINS += chrome_collector_test -#TEST_BINS += crash_collector_test -#TEST_BINS += kernel_collector_test -#TEST_BINS += udev_collector_test -#TEST_BINS += unclean_shutdown_collector_test -#TEST_BINS += user_collector_test - - -# Define a template that defines the rules for a unittest. In case you want -# to add options, etc. for a particular test, the rule to build the test will -# be for "CXX_BINARY()". For example, you could add options with: -# CXX_BINARY(crash_collector_test): CXXFLAGS += -some-other-option -define TEST_RULES_template -CXX_BINARY($(1)): LDLIBS += $(UNITTEST_LIBS) $(DBUS_PC_LIBS) -CXX_BINARY($(1)): $(1).o $(CRASH_OBJS) -clean: CLEAN($(1)) -tests: TEST(CXX_BINARY($(1))) -endef - -CC_BINARY(warn_collector_test): warn_collector_test.o - -# If $(TEST_BINS) is not already set, assume any filename matching *_test.cc -# is a test. -TEST_BINS ?= $(patsubst $(SRC)/%.cc,%,$(wildcard $(SRC)/*_test.cc)) -$(foreach prog,$(TEST_BINS),$(eval $(call TEST_RULES_template,$(prog)))) -tests: TEST(CC_BINARY(warn_collector_test)) diff --git a/crash_reporter/common.mk b/crash_reporter/common.mk deleted file mode 100644 index 50c1dd2b3..000000000 --- a/crash_reporter/common.mk +++ /dev/null @@ -1,924 +0,0 @@ -# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. -# -# If this file is part of another source distribution, it's license may be -# stored in LICENSE.makefile or LICENSE.common.mk. -# -# NOTE NOTE NOTE -# The authoritative common.mk is located in: -# https://chromium.googlesource.com/chromiumos/platform/common-mk.git -# Please make all changes there, then copy into place in other repos. -# NOTE NOTE NOTE -# -# This file provides a common architecture for building C/C++ source trees. -# It uses recursive makefile inclusion to create a single make process which -# can be built in the source tree or with the build artifacts placed elsewhere. -# -# It is fully parallelizable for all targets, including static archives. -# -# To use: -# 1. Place common.mk in your top source level -# 2. In your top-level Makefile, place "include common.mk" at the top -# 3. In all subdirectories, create a 'module.mk' file that starts with: -# include common.mk -# And then contains the remainder of your targets. -# 4. All build targets should look like: -# relative/path/target: relative/path/obj.o -# -# See existing makefiles for rule examples. -# -# Exported macros: -# - cc_binary, cxx_binary provide standard compilation steps for binaries -# - cxx_library, cc_library provide standard compilation steps for -# shared objects. -# All of the above optionally take an argument for extra flags. -# - update_archive creates/updates a given .a target -# -# Instead of using the build macros, most users can just use wrapped targets: -# - CXX_BINARY, CC_BINARY, CC_STATIC_BINARY, CXX_STATIC_BINARY -# - CXX_LIBRARY, CC_LIBRARY, CC_STATIC_LIBRARY, CXX_STATIC_LIBRARY -# - E.g., CXX_BINARY(mahbinary): foo.o -# - object.depends targets may be used when a prerequisite is required for an -# object file. Because object files result in multiple build artifacts to -# handle PIC and PIE weirdness. E.g. -# foo.o.depends: generated/dbus.h -# - TEST(binary) or TEST(CXX_BINARY(binary)) may be used as a prerequisite -# for the tests target to trigger an automated test run. -# - CLEAN(file_or_dir) dependency can be added to 'clean'. -# -# If source code is being generated, rules will need to be registered for -# compiling the objects. This can be done by adding one of the following -# to the Makefile: -# - For C source files -# $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/b.o,CC,c,CFLAGS)) -# - For C++ source files -# $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/b.o,CXX,cc,CXXFLAGS)) -# -# Exported targets meant to have prerequisites added to: -# - all - Your desired targets should be given -# - tests - Any TEST(test_binary) targets should be given -# - FORCE - force the given target to run regardless of changes -# In most cases, using .PHONY is preferred. -# -# Possible command line variables: -# - COLOR=[0|1] to set ANSI color output (default: 1) -# - VERBOSE=[0|1] to hide/show commands (default: 0) -# - MODE=[opt|dbg|profiling] (default: opt) -# opt - Enable optimizations for release builds -# dbg - Turn down optimization for debugging -# profiling - Turn off optimization and turn on profiling/coverage -# support. -# - ARCH=[x86|arm|supported qemu name] (default: from portage or uname -m) -# - SPLITDEBUG=[0|1] splits debug info in target.debug (default: 0) -# If NOSTRIP=1, SPLITDEBUG will never strip the final emitted objects. -# - NOSTRIP=[0|1] determines if binaries are stripped. (default: 1) -# NOSTRIP=0 and MODE=opt will also drop -g from the CFLAGS. -# - VALGRIND=[0|1] runs tests under valgrind (default: 0) -# - OUT=/path/to/builddir puts all output in given path (default: $PWD) -# - VALGRIND_ARGS="" supplies extra memcheck arguments -# -# Per-target(-ish) variable: -# - NEEDS_ROOT=[0|1] allows a TEST() target to run with root. -# Default is 0 unless it is running under QEmu. -# - NEEDS_MOUNTS=[0|1] allows a TEST() target running on QEmu to get -# setup mounts in the $(SYSROOT) -# -# Caveats: -# - Directories or files with spaces in them DO NOT get along with GNU Make. -# If you need them, all uses of dir/notdir/etc will need to have magic -# wrappers. Proceed at risk to your own sanity. -# - External CXXFLAGS and CFLAGS should be passed via the environment since -# this file does not use 'override' to control them. -# - Our version of GNU Make doesn't seem to support the 'private' variable -# annotation, so you can't tag a variable private on a wrapping target. - -# Behavior configuration variables -SPLITDEBUG ?= 0 -NOSTRIP ?= 1 -VALGRIND ?= 0 -COLOR ?= 1 -VERBOSE ?= 0 -MODE ?= opt -ARCH ?= $(shell uname -m) - -# Put objects in a separate tree based on makefile locations -# This means you can build a tree without touching it: -# make -C $SRCDIR # will create ./build-$(MODE) -# Or -# make -C $SRCDIR OUT=$PWD -# This variable is extended on subdir calls and doesn't need to be re-called. -OUT ?= $(PWD)/ - -# Make OUT now so we can use realpath. -$(shell mkdir -p "$(OUT)") - -# TODO(wad) Relative paths are resolved against SRC and not the calling dir. -# Ensure a command-line supplied OUT has a slash -override OUT := $(realpath $(OUT))/ - -# SRC is not meant to be set by the end user, but during make call relocation. -# $(PWD) != $(CURDIR) all the time. -export SRC ?= $(CURDIR) - -# Re-start in the $(OUT) directory if we're not there. -# We may be invoked using -C or bare and we need to ensure behavior -# is consistent so we check both PWD vs OUT and PWD vs CURDIR. -override RELOCATE_BUILD := 0 -ifneq (${PWD}/,${OUT}) -override RELOCATE_BUILD := 1 -endif -# Make sure we're running with no builtin targets. They cause -# leakage and mayhem! -ifneq (${PWD},${CURDIR}) -override RELOCATE_BUILD := 1 -# If we're run from the build dir, don't let it get cleaned up later. -ifeq (${PWD}/,${OUT}) -$(shell touch "$(PWD)/.dont_delete_on_clean") -endif -endif # ifneq (${PWD},${CURDIR} - -# "Relocate" if we need to restart without implicit rules. -ifeq ($(subst r,,$(MAKEFLAGS)),$(MAKEFLAGS)) -override RELOCATE_BUILD := 1 -endif - -ifeq (${RELOCATE_BUILD},1) -# By default, silence build output. Reused below as well. -QUIET = @ -ifeq ($(VERBOSE),1) - QUIET= -endif - -# This target will override all targets, including prerequisites. To avoid -# calling $(MAKE) once per prereq on the given CMDGOAL, we guard it with a local -# variable. -RUN_ONCE := 0 -MAKECMDGOALS ?= all -# Keep the rules split as newer make does not allow them to be declared -# on the same line. But the way :: rules work, the _all here will also -# invoke the %:: rule while retaining "_all" as the default. -_all:: -%:: - $(if $(filter 0,$(RUN_ONCE)), \ - cd "$(OUT)" && \ - $(MAKE) -r -I "$(SRC)" -f "$(CURDIR)/Makefile" \ - SRC="$(CURDIR)" OUT="$(OUT)" $(foreach g,$(MAKECMDGOALS),"$(g)"),) - $(eval RUN_ONCE := 1) -pass-to-subcall := 1 -endif - -ifeq ($(pass-to-subcall),) - -# Only call MODULE if we're in a submodule -MODULES_LIST := $(filter-out Makefile %.d,$(MAKEFILE_LIST)) -ifeq ($(words $(filter-out Makefile common.mk %.d $(SRC)/Makefile \ - $(SRC)/common.mk,$(MAKEFILE_LIST))),0) - -# All the top-level defines outside of module.mk. - -# -# Helper macros -# - -# Create the directory if it doesn't yet exist. -define auto_mkdir - $(if $(wildcard $(dir $1)),$2,$(QUIET)mkdir -p "$(dir $1)") -endef - -# Creates the actual archive with an index. -# The target $@ must end with .pic.a or .pie.a. -define update_archive - $(call auto_mkdir,$(TARGET_OR_MEMBER)) - $(QUIET)# Create the archive in one step to avoid parallel use accessing it - $(QUIET)# before all the symbols are present. - @$(ECHO) "AR $(subst \ -$(SRC)/,,$(^:.o=$(suffix $(basename $(TARGET_OR_MEMBER))).o)) \ --> $(subst $(SRC)/,,$(TARGET_OR_MEMBER))" - $(QUIET)$(AR) rcs $(TARGET_OR_MEMBER) \ - $(subst $(SRC)/,,$(^:.o=$(suffix $(basename $(TARGET_OR_MEMBER))).o)) -endef - -# Default compile from objects using pre-requisites but filters out -# subdirs and .d files. -define cc_binary - $(call COMPILE_BINARY_implementation,CC,$(CFLAGS) $(1),$(EXTRA_FLAGS)) -endef - -define cxx_binary - $(call COMPILE_BINARY_implementation,CXX,$(CXXFLAGS) $(1),$(EXTRA_FLAGS)) -endef - -# Default compile from objects using pre-requisites but filters out -# subdirs and .d files. -define cc_library - $(call COMPILE_LIBRARY_implementation,CC,$(CFLAGS) $(1),$(EXTRA_FLAGS)) -endef -define cxx_library - $(call COMPILE_LIBRARY_implementation,CXX,$(CXXFLAGS) $(1),$(EXTRA_FLAGS)) -endef - -# Deletes files silently if they exist. Meant for use in any local -# clean targets. -define silent_rm - $(if $(wildcard $(1)), - $(QUIET)($(ECHO) -n '$(COLOR_RED)CLEANFILE$(COLOR_RESET) ' && \ - $(ECHO) '$(subst $(OUT)/,,$(wildcard $(1)))' && \ - $(RM) $(1) 2>/dev/null) || true,) -endef -define silent_rmdir - $(if $(wildcard $(1)), - $(if $(wildcard $(1)/*), - $(QUIET)# $(1) not empty [$(wildcard $(1)/*)]. Not deleting., - $(QUIET)($(ECHO) -n '$(COLOR_RED)CLEANDIR$(COLOR_RESET) ' && \ - $(ECHO) '$(subst $(OUT)/,,$(wildcard $(1)))' && \ - $(RMDIR) $(1) 2>/dev/null) || true),) -endef - -# -# Default variable values -# - -# Only override toolchain vars if they are from make. -CROSS_COMPILE ?= -define override_var -ifneq ($(filter undefined default,$(origin $1)),) -$1 = $(CROSS_COMPILE)$2 -endif -endef -$(eval $(call override_var,AR,ar)) -$(eval $(call override_var,CC,gcc)) -$(eval $(call override_var,CXX,g++)) -$(eval $(call override_var,OBJCOPY,objcopy)) -$(eval $(call override_var,PKG_CONFIG,pkg-config)) -$(eval $(call override_var,RANLIB,ranlib)) -$(eval $(call override_var,STRIP,strip)) - -RMDIR ?= rmdir -ECHO = /bin/echo -e - -ifeq ($(lastword $(subst /, ,$(CC))),clang) -CDRIVER = clang -else -CDRIVER = gcc -endif - -ifeq ($(lastword $(subst /, ,$(CXX))),clang++) -CXXDRIVER = clang -else -CXXDRIVER = gcc -endif - -# Internal macro to support check_XXX macros below. -# Usage: $(call check_compile, [code], [compiler], [code_type], [c_flags], -# [extra_c_flags], [library_flags], [success_ret], [fail_ret]) -# Return: [success_ret] if compile succeeded, otherwise [fail_ret] -check_compile = $(shell printf '%b\n' $(1) | \ - $($(2)) $($(4)) -x $(3) $(LDFLAGS) $(5) - $(6) -o /dev/null > /dev/null 2>&1 \ - && echo "$(7)" || echo "$(8)") - -# Helper macro to check whether a test program will compile with the specified -# compiler flags. -# Usage: $(call check_compile_cc, [code], [flags], [alternate_flags]) -# Return: [flags] if compile succeeded, otherwise [alternate_flags] -check_compile_cc = $(call check_compile,$(1),CC,c,CFLAGS,$(2),,$(2),$(3)) -check_compile_cxx = $(call check_compile,$(1),CXX,c++,CXXFLAGS,$(2),,$(2),$(3)) - -# Helper macro to check whether a test program will compile with the specified -# libraries. -# Usage: $(call check_compile_cc, [code], [library_flags], [alternate_flags]) -# Return: [library_flags] if compile succeeded, otherwise [alternate_flags] -check_libs_cc = $(call check_compile,$(1),CC,c,CFLAGS,,$(2),$(2),$(3)) -check_libs_cxx = $(call check_compile,$(1),CXX,c++,CXXFLAGS,,$(2),$(2),$(3)) - -# Helper macro to check whether the compiler accepts the specified flags. -# Usage: $(call check_compile_cc, [flags], [alternate_flags]) -# Return: [flags] if compile succeeded, otherwise [alternate_flags] -check_cc = $(call check_compile_cc,'int main() { return 0; }',$(1),$(2)) -check_cxx = $(call check_compile_cxx,'int main() { return 0; }',$(1),$(2)) - -# Choose the stack protector flags based on whats supported by the compiler. -SSP_CFLAGS := $(call check_cc,-fstack-protector-strong) -ifeq ($(SSP_CFLAGS),) - SSP_CFLAGS := $(call check_cc,-fstack-protector-all) -endif - -# To update these from an including Makefile: -# CXXFLAGS += -mahflag # Append to the list -# CXXFLAGS := -mahflag $(CXXFLAGS) # Prepend to the list -# CXXFLAGS := $(filter-out badflag,$(CXXFLAGS)) # Filter out a value -# The same goes for CFLAGS. -COMMON_CFLAGS-gcc := -fvisibility=internal -ggdb3 -Wa,--noexecstack -COMMON_CFLAGS-clang := -fvisibility=hidden -ggdb -COMMON_CFLAGS := -Wall -Werror -fno-strict-aliasing $(SSP_CFLAGS) -O1 -Wformat=2 -CXXFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CXXDRIVER)) -CFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CDRIVER)) -CPPFLAGS += -D_FORTIFY_SOURCE=2 - - -ifeq ($(MODE),opt) - # Up the optimizations. - CFLAGS := $(filter-out -O1,$(CFLAGS)) -O2 - CXXFLAGS := $(filter-out -O1,$(CXXFLAGS)) -O2 - # Only drop -g* if symbols aren't desired. - ifeq ($(NOSTRIP),0) - # TODO: do we want -fomit-frame-pointer on x86? - CFLAGS := $(filter-out -ggdb3,$(CFLAGS)) - CXXFLAGS := $(filter-out -ggdb3,$(CXXFLAGS)) - endif -endif - -ifeq ($(MODE),profiling) - CFLAGS := $(CFLAGS) -O0 -g --coverage - CXXFLAGS := $(CXXFLAGS) -O0 -g --coverage - LDFLAGS := $(LDFLAGS) --coverage -endif - -LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,noexecstack -Wl,-z,now - -# Fancy helpers for color if a prompt is defined -ifeq ($(COLOR),1) -COLOR_RESET = \x1b[0m -COLOR_GREEN = \x1b[32;01m -COLOR_RED = \x1b[31;01m -COLOR_YELLOW = \x1b[33;01m -endif - -# By default, silence build output. -QUIET = @ -ifeq ($(VERBOSE),1) - QUIET= -endif - -# -# Implementation macros for compile helpers above -# - -# Useful for dealing with pie-broken toolchains. -# Call make with PIE=0 to disable default PIE use. -OBJ_PIE_FLAG = -fPIE -COMPILE_PIE_FLAG = -pie -ifeq ($(PIE),0) - OBJ_PIE_FLAG = - COMPILE_PIE_FLAG = -endif - -# Favor member targets first for CXX_BINARY(%) magic. -# And strip out nested members if possible. -LP := ( -RP := ) -TARGET_OR_MEMBER = $(lastword $(subst $(LP), ,$(subst $(RP),,$(or $%,$@)))) - -# Default compile from objects using pre-requisites but filters out -# all non-.o files. -define COMPILE_BINARY_implementation - @$(ECHO) "LD$(1) $(subst $(PWD)/,,$(TARGET_OR_MEMBER))" - $(call auto_mkdir,$(TARGET_OR_MEMBER)) - $(QUIET)$($(1)) $(COMPILE_PIE_FLAGS) -o $(TARGET_OR_MEMBER) \ - $(2) $(LDFLAGS) \ - $(filter %.o %.a,$(^:.o=.pie.o)) \ - $(foreach so,$(filter %.so,$^),-L$(dir $(so)) \ - -l$(patsubst lib%,%,$(basename $(notdir $(so))))) \ - $(LDLIBS) - $(call conditional_strip) - @$(ECHO) -n "BIN " - @$(ECHO) "$(COLOR_GREEN)$(subst $(PWD)/,,$(TARGET_OR_MEMBER))$(COLOR_RESET)" - @$(ECHO) " $(COLOR_YELLOW)-----$(COLOR_RESET)" -endef - -# TODO: add version support extracted from PV environment variable -#ifeq ($(PV),9999) -#$(warning PV=$(PV). If shared object versions matter, please force PV=.) -#endif -# Then add -Wl,-soname,$@.$(PV) ? - -# Default compile from objects using pre-requisites but filters out -# all non-.o values. (Remember to add -L$(OUT) -llib) -COMMA := , -define COMPILE_LIBRARY_implementation - @$(ECHO) "SHARED$(1) $(subst $(PWD)/,,$(TARGET_OR_MEMBER))" - $(call auto_mkdir,$(TARGET_OR_MEMBER)) - $(QUIET)$($(1)) -shared -Wl,-E -o $(TARGET_OR_MEMBER) \ - $(2) $(LDFLAGS) \ - $(if $(filter %.a,$^),-Wl$(COMMA)--whole-archive,) \ - $(filter %.o ,$(^:.o=.pic.o)) \ - $(foreach a,$(filter %.a,$^),-L$(dir $(a)) \ - -l$(patsubst lib%,%,$(basename $(notdir $(a))))) \ - $(foreach so,$(filter %.so,$^),-L$(dir $(so)) \ - -l$(patsubst lib%,%,$(basename $(notdir $(so))))) \ - $(LDLIBS) - $(call conditional_strip) - @$(ECHO) -n "LIB $(COLOR_GREEN)" - @$(ECHO) "$(subst $(PWD)/,,$(TARGET_OR_MEMBER))$(COLOR_RESET)" - @$(ECHO) " $(COLOR_YELLOW)-----$(COLOR_RESET)" -endef - -define conditional_strip - $(if $(filter 0,$(NOSTRIP)),$(call strip_artifact)) -endef - -define strip_artifact - @$(ECHO) "STRIP $(subst $(OUT)/,,$(TARGET_OR_MEMBER))" - $(if $(filter 1,$(SPLITDEBUG)), @$(ECHO) -n "DEBUG "; \ - $(ECHO) "$(COLOR_YELLOW)\ -$(subst $(OUT)/,,$(TARGET_OR_MEMBER)).debug$(COLOR_RESET)") - $(if $(filter 1,$(SPLITDEBUG)), \ - $(QUIET)$(OBJCOPY) --only-keep-debug "$(TARGET_OR_MEMBER)" \ - "$(TARGET_OR_MEMBER).debug") - $(if $(filter-out dbg,$(MODE)),$(QUIET)$(STRIP) --strip-unneeded \ - "$(TARGET_OR_MEMBER)",) -endef - -# -# Global pattern rules -# - -# Below, the archive member syntax is abused to create fancier -# syntactic sugar for recipe authors that avoids needed to know -# subcall options. The downside is that make attempts to look -# into the phony archives for timestamps. This will cause the final -# target to be rebuilt/linked on _every_ call to make even when nothing -# has changed. Until a better way presents itself, we have helpers that -# do the stat check on make's behalf. Dodgy but simple. -define old_or_no_timestamp - $(if $(realpath $%),,$(1)) - $(if $(shell find $^ -cnewer "$%" 2>/dev/null),$(1)) -endef - -define check_deps - $(if $(filter 0,$(words $^)),\ - $(error Missing dependencies or declaration of $@($%)),) -endef - -# Build a cxx target magically -CXX_BINARY(%): - $(call check_deps) - $(call old_or_no_timestamp,$(call cxx_binary)) -clean: CLEAN(CXX_BINARY*) - -CC_BINARY(%): - $(call check_deps) - $(call old_or_no_timestamp,$(call cc_binary)) -clean: CLEAN(CC_BINARY*) - -CXX_STATIC_BINARY(%): - $(call check_deps) - $(call old_or_no_timestamp,$(call cxx_binary,-static)) -clean: CLEAN(CXX_STATIC_BINARY*) - -CC_STATIC_BINARY(%): - $(call check_deps) - $(call old_or_no_timestamp,$(call cc_binary,-static)) -clean: CLEAN(CC_STATIC_BINARY*) - -CXX_LIBRARY(%): - $(call check_deps) - $(call old_or_no_timestamp,$(call cxx_library)) -clean: CLEAN(CXX_LIBRARY*) - -CXX_LIBARY(%): - $(error Typo alert! LIBARY != LIBRARY) - -CC_LIBRARY(%): - $(call check_deps) - $(call old_or_no_timestamp,$(call cc_library)) -clean: CLEAN(CC_LIBRARY*) - -CC_LIBARY(%): - $(error Typo alert! LIBARY != LIBRARY) - -CXX_STATIC_LIBRARY(%): - $(call check_deps) - $(call old_or_no_timestamp,$(call update_archive)) -clean: CLEAN(CXX_STATIC_LIBRARY*) - -CXX_STATIC_LIBARY(%): - $(error Typo alert! LIBARY != LIBRARY) - -CC_STATIC_LIBRARY(%): - $(call check_deps) - $(call old_or_no_timestamp,$(call update_archive)) -clean: CLEAN(CC_STATIC_LIBRARY*) - -CC_STATIC_LIBARY(%): - $(error Typo alert! LIBARY != LIBRARY) - - -TEST(%): % qemu_chroot_install - $(call TEST_implementation) -.PHONY: TEST - -# multiple targets with a wildcard need to share an directory. -# Don't use this directly it just makes sure the directory is removed _after_ -# the files are. -CLEANFILE(%): - $(call silent_rm,$(TARGET_OR_MEMBER)) -.PHONY: CLEANFILE - -CLEAN(%): CLEANFILE(%) - $(QUIET)# CLEAN($%) meta-target called - $(if $(filter-out $(PWD)/,$(dir $(abspath $(TARGET_OR_MEMBER)))), \ - $(call silent_rmdir,$(dir $(abspath $(TARGET_OR_MEMBER)))),\ - $(QUIET)# Not deleting $(dir $(abspath $(TARGET_OR_MEMBER))) yet.) -.PHONY: CLEAN - -# -# Top-level objects and pattern rules -# - -# All objects for .c files at the top level -C_OBJECTS = $(patsubst $(SRC)/%.c,%.o,$(wildcard $(SRC)/*.c)) - - -# All objects for .cxx files at the top level -CXX_OBJECTS = $(patsubst $(SRC)/%.cc,%.o,$(wildcard $(SRC)/*.cc)) - -# Note, the catch-all pattern rules don't work in subdirectories because -# we're building from the $(OUT) directory. At the top-level (here) they will -# work, but we go ahead and match using the module form. Then we can place a -# generic pattern rule to capture leakage from the main Makefile. (Later in the -# file.) -# -# The reason target specific pattern rules work well for modules, -# MODULE_C_OBJECTS, is because it scopes the behavior to the given target which -# ensures we get a relative directory offset from $(OUT) which otherwise would -# not match without further magic on a per-subdirectory basis. - -# Creates object file rules. Call with eval. -# $(1) list of .o files -# $(2) source type (CC or CXX) -# $(3) source suffix (cc or c) -# $(4) compiler flag name (CFLAGS or CXXFLAGS) -# $(5) source dir: _only_ if $(SRC). Leave blank for obj tree. -define add_object_rules -$(patsubst %.o,%.pie.o,$(1)): %.pie.o: $(5)%.$(3) %.o.depends - $$(call auto_mkdir,$$@) - $$(call OBJECT_PATTERN_implementation,$(2),\ - $$(basename $$@),$$($(4)) $$(CPPFLAGS) $$(OBJ_PIE_FLAG)) - -$(patsubst %.o,%.pic.o,$(1)): %.pic.o: $(5)%.$(3) %.o.depends - $$(call auto_mkdir,$$@) - $$(call OBJECT_PATTERN_implementation,$(2),\ - $$(basename $$@),$$($(4)) $$(CPPFLAGS) -fPIC) - -# Placeholder for depends -$(patsubst %.o,%.o.depends,$(1)): - $$(call auto_mkdir,$$@) - $$(QUIET)touch "$$@" - -$(1): %.o: %.pic.o %.pie.o - $$(call auto_mkdir,$$@) - $$(QUIET)touch "$$@" -endef - -define OBJECT_PATTERN_implementation - @$(ECHO) "$(1) $(subst $(SRC)/,,$<) -> $(2).o" - $(call auto_mkdir,$@) - $(QUIET)$($(1)) -c -MD -MF $(2).d $(3) -o $(2).o $< - $(QUIET)# Wrap all the deps in $$(wildcard) so a missing header - $(QUIET)# won't cause weirdness. First we remove newlines and \, - $(QUIET)# then wrap it. - $(QUIET)sed -i -e :j -e '$$!N;s|\\\s*\n| |;tj' \ - -e 's|^\(.*\s*:\s*\)\(.*\)$$|\1 $$\(wildcard \2\)|' $(2).d -endef - -# Now actually register handlers for C(XX)_OBJECTS. -$(eval $(call add_object_rules,$(C_OBJECTS),CC,c,CFLAGS,$(SRC)/)) -$(eval $(call add_object_rules,$(CXX_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/)) - -# Disable default pattern rules to help avoid leakage. -# These may already be handled by '-r', but let's keep it to be safe. -%: %.o ; -%.a: %.o ; -%.o: %.c ; -%.o: %.cc ; - -# NOTE: A specific rule for archive objects is avoided because parallel -# update of the archive causes build flakiness. -# Instead, just make the objects the prerequisites and use update_archive -# To use the foo.a(obj.o) functionality, targets would need to specify the -# explicit object they expect on the prerequisite line. - -# -# Architecture detection and QEMU wrapping -# - -HOST_ARCH ?= $(shell uname -m) -override ARCH := $(strip $(ARCH)) -override HOST_ARCH := $(strip $(HOST_ARCH)) -# emake will supply "x86" or "arm" for ARCH, but -# if uname -m runs and you get x86_64, then this subst -# will break. -ifeq ($(subst x86,i386,$(ARCH)),i386) - QEMU_ARCH := $(subst x86,i386,$(ARCH)) # x86 -> i386 -else ifeq ($(subst amd64,x86_64,$(ARCH)),x86_64) - QEMU_ARCH := $(subst amd64,x86_64,$(ARCH)) # amd64 -> x86_64 -else - QEMU_ARCH = $(ARCH) -endif -override QEMU_ARCH := $(strip $(QEMU_ARCH)) - -# If we're cross-compiling, try to use qemu for running the tests. -ifneq ($(QEMU_ARCH),$(HOST_ARCH)) - ifeq ($(SYSROOT),) - $(info SYSROOT not defined. qemu-based testing disabled) - else - # A SYSROOT is assumed for QEmu use. - USE_QEMU ?= 1 - - # Allow 64-bit hosts to run 32-bit without qemu. - ifeq ($(HOST_ARCH),x86_64) - ifeq ($(QEMU_ARCH),i386) - USE_QEMU = 0 - endif - endif - endif -else - USE_QEMU ?= 0 -endif - -# Normally we don't need to run as root or do bind mounts, so only -# enable it by default when we're using QEMU. -NEEDS_ROOT ?= $(USE_QEMU) -NEEDS_MOUNTS ?= $(USE_QEMU) - -SYSROOT_OUT = $(OUT) -ifneq ($(SYSROOT),) - SYSROOT_OUT = $(subst $(SYSROOT),,$(OUT)) -else - # Default to / when all the empty-sysroot logic is done. - SYSROOT = / -endif - -QEMU_NAME = qemu-$(QEMU_ARCH) -QEMU_PATH = /build/bin/$(QEMU_NAME) -QEMU_SYSROOT_PATH = $(SYSROOT)$(QEMU_PATH) -QEMU_SRC_PATH = /usr/bin/$(QEMU_NAME) -QEMU_BINFMT_PATH = /proc/sys/fs/binfmt_misc/$(QEMU_NAME) -QEMU_REGISTER_PATH = /proc/sys/fs/binfmt_misc/register - -QEMU_MAGIC_arm = ":$(QEMU_NAME):M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/build/bin/qemu-arm:" - - -# -# Output full configuration at top level -# - -# Don't show on clean -ifneq ($(MAKECMDGOALS),clean) - $(info build configuration:) - $(info - OUT=$(OUT)) - $(info - SRC=$(SRC)) - $(info - MODE=$(MODE)) - $(info - SPLITDEBUG=$(SPLITDEBUG)) - $(info - NOSTRIP=$(NOSTRIP)) - $(info - VALGRIND=$(VALGRIND)) - $(info - COLOR=$(COLOR)) - $(info - ARCH=$(ARCH)) - $(info - QEMU_ARCH=$(QEMU_ARCH)) - $(info - USE_QEMU=$(USE_QEMU)) - $(info - NEEDS_ROOT=$(NEEDS_ROOT)) - $(info - NEEDS_MOUNTS=$(NEEDS_MOUNTS)) - $(info - SYSROOT=$(SYSROOT)) - $(info ) -endif - -# -# Standard targets with detection for when they are improperly configured. -# - -# all does not include tests by default -all: - $(QUIET)(test -z "$^" && \ - $(ECHO) "You must add your targets as 'all' prerequisites") || true - $(QUIET)test -n "$^" - -# Builds and runs tests for the target arch -# Run them in parallel -# After the test have completed, if profiling, run coverage analysis -tests: -ifeq ($(MODE),profiling) - @$(ECHO) "COVERAGE [$(COLOR_YELLOW)STARTED$(COLOR_RESET)]" - $(QUIET)FILES=""; \ - for GCNO in `find . -name "*.gcno"`; do \ - GCDA="$${GCNO%.gcno}.gcda"; \ - if [ -e $${GCDA} ]; then \ - FILES="$${FILES} $${GCDA}"; \ - fi \ - done; \ - if [ -n "$${FILES}" ]; then \ - gcov -l $${FILES}; \ - lcov --capture --directory . \ - --output-file=lcov-coverage.info; \ - genhtml lcov-coverage.info \ - --output-directory lcov-html; \ - fi - @$(ECHO) "COVERAGE [$(COLOR_YELLOW)FINISHED$(COLOR_RESET)]" -endif -.PHONY: tests - -qemu_chroot_install: -ifeq ($(USE_QEMU),1) - $(QUIET)$(ECHO) "QEMU Preparing $(QEMU_NAME)" - @# Copying strategy - @# Compare /usr/bin/qemu inode to /build/$board/build/bin/qemu, if different - @# hard link to a temporary file, then rename temp to target. This should - @# ensure that once $QEMU_SYSROOT_PATH exists it will always exist, regardless - @# of simultaneous test setups. - $(QUIET)if [[ ! -e $(QEMU_SYSROOT_PATH) || \ - `stat -c %i $(QEMU_SRC_PATH)` != `stat -c %i $(QEMU_SYSROOT_PATH)` \ - ]]; then \ - $(ROOT_CMD) ln -Tf $(QEMU_SRC_PATH) $(QEMU_SYSROOT_PATH).$$$$; \ - $(ROOT_CMD) mv -Tf $(QEMU_SYSROOT_PATH).$$$$ $(QEMU_SYSROOT_PATH); \ - fi - - @# Prep the binfmt handler. First mount if needed, then unregister any bad - @# mappings and then register our mapping. - @# There may still be some race conditions here where one script de-registers - @# and another script starts executing before it gets re-registered, however - @# it should be rare. - -$(QUIET)[[ -e $(QEMU_REGISTER_PATH) ]] || \ - $(ROOT_CMD) mount binfmt_misc -t binfmt_misc \ - /proc/sys/fs/binfmt_misc - - -$(QUIET)if [[ -e $(QEMU_BINFMT_PATH) && \ - `awk '$$1 == "interpreter" {print $$NF}' $(QEMU_BINFMT_PATH)` != \ - "$(QEMU_PATH)" ]]; then \ - echo -1 | $(ROOT_CMD) tee $(QEMU_BINFMT_PATH) >/dev/null; \ - fi - - -$(if $(QEMU_MAGIC_$(ARCH)),$(QUIET)[[ -e $(QEMU_BINFMT_PATH) ]] || \ - echo $(QEMU_MAGIC_$(ARCH)) | $(ROOT_CMD) tee $(QEMU_REGISTER_PATH) \ - >/dev/null) -endif -.PHONY: qemu_clean qemu_chroot_install - -# TODO(wad) Move to -L $(SYSROOT) and fakechroot when qemu-user -# doesn't hang traversing /proc from SYSROOT. -SUDO_CMD = sudo -UNSHARE_CMD = unshare -QEMU_CMD = -ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),$(SUDO_CMD) , ) -MOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) mount, \#) -UMOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) umount, \#) -QEMU_LDPATH = $(SYSROOT_LDPATH):/lib64:/lib:/usr/lib64:/usr/lib -ROOT_CMD_LDPATH = $(SYSROOT_LDPATH):$(SYSROOT)/lib64: -ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/lib:$(SYSROOT)/usr/lib64: -ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/usr/lib -ifeq ($(USE_QEMU),1) - export QEMU_CMD = \ - $(SUDO_CMD) chroot $(SYSROOT) $(QEMU_PATH) \ - -drop-ld-preload \ - -E LD_LIBRARY_PATH="$(QEMU_LDPATH):$(patsubst $(OUT),,$(LD_DIRS))" \ - -E HOME="$(HOME)" -E SRC="$(SRC)" -- - # USE_QEMU conditional function - define if_qemu - $(1) - endef -else - ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),sudo, ) \ - LD_LIBRARY_PATH="$(ROOT_CMD_LDPATH):$(LD_DIRS)" - define if_qemu - $(2) - endef -endif - -VALGRIND_CMD = -ifeq ($(VALGRIND),1) - VALGRIND_CMD = /usr/bin/valgrind --tool=memcheck $(VALGRIND_ARGS) -- -endif - -define TEST_implementation - $(QUIET)$(call TEST_setup) - $(QUIET)$(call TEST_run) - $(QUIET)$(call TEST_teardown) - $(QUIET)exit $$(cat $(OUT)$(TARGET_OR_MEMBER).status.test) -endef - -define TEST_setup - @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " - @$(ECHO) "[$(COLOR_YELLOW)SETUP$(COLOR_RESET)]" - $(QUIET)# Setup a target-specific results file - $(QUIET)(echo > $(OUT)$(TARGET_OR_MEMBER).setup.test) - $(QUIET)(echo 1 > $(OUT)$(TARGET_OR_MEMBER).status.test) - $(QUIET)(echo > $(OUT)$(TARGET_OR_MEMBER).cleanup.test) - $(QUIET)# No setup if we are not using QEMU - $(QUIET)# TODO(wad) this is racy until we use a vfs namespace - $(call if_qemu,\ - $(QUIET)(echo "mkdir -p '$(SYSROOT)/proc' '$(SYSROOT)/dev' \ - '$(SYSROOT)/mnt/host/source'" \ - >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) - $(call if_qemu,\ - $(QUIET)(echo "$(MOUNT_CMD) --bind /mnt/host/source \ - '$(SYSROOT)/mnt/host/source'" \ - >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) - $(call if_qemu,\ - $(QUIET)(echo "$(MOUNT_CMD) --bind /proc '$(SYSROOT)/proc'" \ - >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) - $(call if_qemu,\ - $(QUIET)(echo "$(MOUNT_CMD) --bind /dev '$(SYSROOT)/dev'" \ - >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) -endef - -define TEST_teardown - @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " - @$(ECHO) "[$(COLOR_YELLOW)TEARDOWN$(COLOR_RESET)]" - $(call if_qemu, $(QUIET)$(SHELL) "$(OUT)$(TARGET_OR_MEMBER).cleanup.test") -endef - -# Use GTEST_ARGS.[arch] if defined. -override GTEST_ARGS.real = \ - $(call if_qemu,$(GTEST_ARGS.qemu.$(QEMU_ARCH)),$(GTEST_ARGS.host.$(HOST_ARCH))) - -define TEST_run - @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " - @$(ECHO) "[$(COLOR_GREEN)RUN$(COLOR_RESET)]" - $(QUIET)(echo 1 > "$(OUT)$(TARGET_OR_MEMBER).status.test") - $(QUIET)(echo $(ROOT_CMD) SRC="$(SRC)" $(QEMU_CMD) $(VALGRIND_CMD) \ - "$(strip $(call if_qemu, $(SYSROOT_OUT),$(OUT))$(TARGET_OR_MEMBER))" \ - $(if $(filter-out 0,$(words $(GTEST_ARGS.real))),$(GTEST_ARGS.real),\ - $(GTEST_ARGS)) >> "$(OUT)$(TARGET_OR_MEMBER).setup.test") - -$(QUIET)$(call if_qemu,$(SUDO_CMD) $(UNSHARE_CMD) -m) $(SHELL) \ - $(OUT)$(TARGET_OR_MEMBER).setup.test \ - && echo 0 > "$(OUT)$(TARGET_OR_MEMBER).status.test" -endef - -# Recursive list reversal so that we get RMDIR_ON_CLEAN in reverse order. -define reverse -$(if $(1),$(call reverse,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1)) -endef - -clean: qemu_clean -clean: CLEAN($(OUT)*.d) CLEAN($(OUT)*.o) CLEAN($(OUT)*.debug) -clean: CLEAN($(OUT)*.test) CLEAN($(OUT)*.depends) -clean: CLEAN($(OUT)*.gcno) CLEAN($(OUT)*.gcda) CLEAN($(OUT)*.gcov) -clean: CLEAN($(OUT)lcov-coverage.info) CLEAN($(OUT)lcov-html) - -clean: - $(QUIET)# Always delete the containing directory last. - $(call silent_rmdir,$(OUT)) - -FORCE: ; -# Empty rule for use when no special targets are needed, like large_tests -NONE: - -.PHONY: clean NONE valgrind NONE -.DEFAULT_GOAL := all -# Don't let make blow away "intermediates" -.PRECIOUS: %.pic.o %.pie.o %.a %.pic.a %.pie.a %.test - -# Start accruing build info -OUT_DIRS = $(OUT) -LD_DIRS = $(OUT) -SRC_DIRS = $(SRC) - -include $(wildcard $(OUT)*.d) -SUBMODULE_DIRS = $(wildcard $(SRC)/*/module.mk) -include $(SUBMODULE_DIRS) - - -else ## In duplicate inclusions of common.mk - -# Get the current inclusion directory without a trailing slash -MODULE := $(patsubst %/,%, \ - $(dir $(lastword $(filter-out %common.mk,$(MAKEFILE_LIST))))) -MODULE := $(subst $(SRC)/,,$(MODULE)) -MODULE_NAME := $(subst /,_,$(MODULE)) -#VPATH := $(MODULE):$(VPATH) - - -# Depth first -$(eval OUT_DIRS += $(OUT)$(MODULE)) -$(eval SRC_DIRS += $(OUT)$(MODULE)) -$(eval LD_DIRS := $(LD_DIRS):$(OUT)$(MODULE)) - -# Add the defaults from this dir to rm_clean -clean: CLEAN($(OUT)$(MODULE)/*.d) CLEAN($(OUT)$(MODULE)/*.o) -clean: CLEAN($(OUT)$(MODULE)/*.debug) CLEAN($(OUT)$(MODULE)/*.test) -clean: CLEAN($(OUT)$(MODULE)/*.depends) -clean: CLEAN($(OUT)$(MODULE)/*.gcno) CLEAN($(OUT)$(MODULE)/*.gcda) -clean: CLEAN($(OUT)$(MODULE)/*.gcov) CLEAN($(OUT)lcov-coverage.info) -clean: CLEAN($(OUT)lcov-html) - -$(info + submodule: $(MODULE_NAME)) -# We must eval otherwise they may be dropped. -MODULE_C_OBJECTS = $(patsubst $(SRC)/$(MODULE)/%.c,$(MODULE)/%.o,\ - $(wildcard $(SRC)/$(MODULE)/*.c)) -$(eval $(MODULE_NAME)_C_OBJECTS ?= $(MODULE_C_OBJECTS)) -MODULE_CXX_OBJECTS = $(patsubst $(SRC)/$(MODULE)/%.cc,$(MODULE)/%.o,\ - $(wildcard $(SRC)/$(MODULE)/*.cc)) -$(eval $(MODULE_NAME)_CXX_OBJECTS ?= $(MODULE_CXX_OBJECTS)) - -# Note, $(MODULE) is implicit in the path to the %.c. -# See $(C_OBJECTS) for more details. -# Register rules for the module objects. -$(eval $(call add_object_rules,$(MODULE_C_OBJECTS),CC,c,CFLAGS,$(SRC)/)) -$(eval $(call add_object_rules,$(MODULE_CXX_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/)) - -# Continue recursive inclusion of module.mk files -SUBMODULE_DIRS = $(wildcard $(SRC)/$(MODULE)/*/module.mk) -include $(wildcard $(OUT)$(MODULE)/*.d) -include $(SUBMODULE_DIRS) - -endif -endif ## pass-to-subcall wrapper for relocating the call directory diff --git a/crash_reporter/make_tests.sh b/crash_reporter/make_tests.sh deleted file mode 100755 index 609069ec9..000000000 --- a/crash_reporter/make_tests.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Builds tests. - -set -e - -SOURCE_DIR=$(readlink -f $(dirname $0)) -pushd "$SCRIPT_DIR" -make tests -mkdir -p "${OUT_DIR}" -cp *_test "${OUT_DIR}" -popd From 82deea83b097793263295dfe5b09c9657daa6a4d Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Tue, 11 Feb 2014 20:46:48 -0800 Subject: [PATCH 151/225] Update metrics dependency to use slotted libmetrics library. BUG=chromium:342866 CQ-DEPEND=CL:185187 CQ-DEPEND=CL:186026 TEST=Trybot run on paladin, release, and chromiumos-sdk builders. Change-Id: I54f3f2055293160ad807c48a126e411c6188b6f9 Reviewed-on: https://chromium-review.googlesource.com/186028 Reviewed-by: Mike Frysinger Tested-by: Ben Chan Commit-Queue: Ben Chan --- crash_reporter/crash-reporter.gyp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index 39835fa82..0b9d9b109 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -60,7 +60,7 @@ 'dependencies': [ 'libcrash', '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', - '../metrics/metrics.gyp:libmetrics', + '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', ], 'sources': [ 'crash_reporter.cc', @@ -95,7 +95,7 @@ ], }, 'dependencies': [ - '../metrics/metrics.gyp:libmetrics', + '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', ], 'sources': [ 'warn_collector.l', From b20c9ef8e90a7e7584b83faa25986b4c08021389 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Tue, 17 Dec 2013 18:12:09 -0800 Subject: [PATCH 152/225] Push full warning signature instead of its hash only. A previous change computed a signature including the function name and offset, but failed to send it to the crash server. This fixes the problem. BUG=chromium:328948 TEST=none Change-Id: I2ff2e548ee1a8feebd6352433c9bd0f96076f15d Reviewed-on: https://chromium-review.googlesource.com/180561 Reviewed-by: Luigi Semenzato Tested-by: Luigi Semenzato Commit-Queue: Luigi Semenzato --- crash_reporter/kernel_warning_collector.cc | 18 +++++++++--------- crash_reporter/kernel_warning_collector.h | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crash_reporter/kernel_warning_collector.cc b/crash_reporter/kernel_warning_collector.cc index f8188c7fb..34d29a58f 100644 --- a/crash_reporter/kernel_warning_collector.cc +++ b/crash_reporter/kernel_warning_collector.cc @@ -28,19 +28,19 @@ KernelWarningCollector::~KernelWarningCollector() { } bool KernelWarningCollector::LoadKernelWarning(std::string *content, - std::string *hash_string) { + std::string *signature) { FilePath kernel_warning_path(kKernelWarningPath); if (!base::ReadFileToString(kernel_warning_path, content)) { LOG(ERROR) << "Could not open " << kKernelWarningPath; return false; } - /* Verify that the first line contains an 8-digit hex hash. */ - *hash_string = content->substr(0, 8); - std::vector output; - if (!base::HexStringToBytes(*hash_string, &output)) { - LOG(ERROR) << "Bad hash " << *hash_string << " in " << kKernelWarningPath; + /* The signature is in the first line. */ + std::string::size_type end_position = content->find('\n'); + if (end_position == std::string::npos) { + LOG(ERROR) << "unexpected kernel warning format"; return false; } + *signature = content->substr(0, end_position); return true; } @@ -62,8 +62,8 @@ bool KernelWarningCollector::Collect() { } std::string kernel_warning; - std::string warning_hash; - if (!LoadKernelWarning(&kernel_warning, &warning_hash)) { + std::string warning_signature; + if (!LoadKernelWarning(&kernel_warning, &warning_signature)) { return true; } @@ -89,7 +89,7 @@ bool KernelWarningCollector::Collect() { return true; } - AddCrashMetaData(kKernelWarningSignatureKey, warning_hash); + AddCrashMetaData(kKernelWarningSignatureKey, warning_signature); WriteCrashMetaData( root_crash_directory.Append( StringPrintf("%s.meta", dump_basename.c_str())), diff --git a/crash_reporter/kernel_warning_collector.h b/crash_reporter/kernel_warning_collector.h index 2f8c793b8..7a5041617 100644 --- a/crash_reporter/kernel_warning_collector.h +++ b/crash_reporter/kernel_warning_collector.h @@ -24,8 +24,8 @@ class KernelWarningCollector : public CrashCollector { friend class KernelWarningCollectorTest; FRIEND_TEST(KernelWarningCollectorTest, CollectOK); - // Reads the full content of the kernel warn dump and the warning hash. - bool LoadKernelWarning(std::string *hash, std::string *content); + // Reads the full content of the kernel warn dump and its signature. + bool LoadKernelWarning(std::string *content, std::string *signature); }; #endif // _CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ From 325a2e313da99c2ab4839462d7ed8de930c7b903 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Fri, 14 Feb 2014 10:41:18 -0800 Subject: [PATCH 153/225] crash reporter: avoid repeated warning collection The warning collector daemon parses the entire syslog every time it starts (almost exclusively at boot). This change makes the collector start reading at the end of /var/log/messages (where it waits for further input) rather than the beginning. The collector will miss any warnings that may have occurred between the last warning reported, and the daemon restart. BUG=chromium:343954 TEST=none yet BRANCH=none Change-Id: I5e9b141561365b9a1d328ed6283c27e8b3369a6f Reviewed-on: https://chromium-review.googlesource.com/186605 Reviewed-by: Grant Grundler Commit-Queue: Luigi Semenzato Tested-by: Luigi Semenzato --- crash_reporter/warn_collector.l | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l index 6411b4d9b..691ef9913 100644 --- a/crash_reporter/warn_collector.l +++ b/crash_reporter/warn_collector.l @@ -160,6 +160,13 @@ static void WarnOpenInput(const char *path) { if (yyin_fd < 0) Die("could not open %s: %s\n", path, strerror(errno)); if (!fifo) { + /* Go directly to the end of the file. We don't want to parse the same + * warnings multiple times on reboot/restart. We might miss some + * warnings, but so be it---it's too hard to keep track reliably of the + * last parsed position in the syslog. + */ + if (lseek(yyin_fd, 0, SEEK_END) < 0) + Die("could not lseek %s: %s\n", path, strerror(errno)); /* Set up notification of file growth and rename. */ i_fd = inotify_init(); if (i_fd < 0) From 9f52040ac95fe6180b6c1a95835eb15c439c5381 Mon Sep 17 00:00:00 2001 From: Yunlian Jiang Date: Thu, 27 Feb 2014 14:26:15 -0800 Subject: [PATCH 154/225] crash-reporter: convert number to int before calling abs() BUG=chromium:347721 TEST=emerge-x86-generic crash-reporter passes. Change-Id: Id37528c44d5c318b917932ebc755eb6b4ca01e8f Reviewed-on: https://chromium-review.googlesource.com/188212 Reviewed-by: Mike Frysinger Commit-Queue: Yunlian Jiang Tested-by: Yunlian Jiang --- crash_reporter/kernel_collector.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 17d150ed7..7f7c7fb95 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -392,7 +392,8 @@ bool KernelCollector::FindCrashingFunction( return false; } if (stack_trace_timestamp != 0 && - abs(stack_trace_timestamp - timestamp) > kSignatureTimestampWindow) { + abs(static_cast(stack_trace_timestamp - timestamp)) + > kSignatureTimestampWindow) { if (print_diagnostics) { printf("Found crashing function but not within window.\n"); } From d24164e2558095239c8a10a1817476db1079a38b Mon Sep 17 00:00:00 2001 From: "B. simonnet" Date: Tue, 18 Feb 2014 17:41:17 -0800 Subject: [PATCH 155/225] Use GOOGLE_CRASH_ID as the default crash report id crash_sender will use crash id and crash version id, in order: * the product name and product version in the meta file if present or * GOOGLE_CRASH_ID and GOOGLE_CRASH_VERSION_ID from /etc/os-release or * ID and VERSION_ID from /etc/os-release * ChromeOS and chrome os version otherwise BUG=chromium:343615 TEST=Built chromeos-base/platform2 and deployed it to a Chromebook. Added a test report in /var/spool/crash. Ran crash_sender in MOCK_CRASH_SENDING mode Changed the values of variable in /etc/os-release and observed that the sent values were correct TEST=Set GOOGLE_CRASH_ID to Brillo, remove protection for test images, observe that the crashes got logged in the Brillo bucket. TEST=trybot on x86, amd64, daisy and duck Change-Id: I30b87a344e7868e4a937c79b89c6d07ff0fcc6e4 Reviewed-on: https://chromium-review.googlesource.com/187035 Reviewed-by: Mike Frysinger Commit-Queue: Bertrand Simonnet Tested-by: Bertrand Simonnet --- crash_reporter/crash_sender | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 1764ba832..3b4f18361 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -6,7 +6,7 @@ set -e -# Product ID in crash report +# Default product ID in crash report (used if GOOGLE_CRASH_* is undefined). CHROMEOS_PRODUCT=ChromeOS # Should remove the run file when this process finishes. We don't want @@ -336,10 +336,31 @@ send_crash() { done # When uploading Chrome reports we need to report the right product and - # version, so allow the meta file to override these values. + # version. If the meta file does not specify it, use GOOGLE_CRASH_ID + # as the product and GOOGLE_CRASH_VERSION_ID as the version. if [ "${product}" = "undefined" ]; then - product=${CHROMEOS_PRODUCT} - version=${chromeos_version} + product="$(get_key_value /etc/os-release 'GOOGLE_CRASH_ID')" + fi + if [ "${version}" = "undefined" ]; then + version="$(get_key_value /etc/os-release 'GOOGLE_CRASH_VERSION_ID')" + fi + + # If GOOGLE_CRASH_* is undefined, we look for ID and VERSION_ID in + # /etc/os-release. + if [ "${product}" = "undefined" ]; then + product="$(get_key_value /etc/os-release 'ID')" + fi + if [ "${version}" = "undefined" ]; then + version="$(get_key_value /etc/os-release 'VERSION_ID')" + fi + + # If ID or VERSION_ID is undefined, we use the default product name + # and CHROMEOS_RELEASE_VERSION from /etc/lsb-release. + if [ "${product}" = "undefined" ]; then + product="${CHROMEOS_PRODUCT}" + fi + if [ "${version}" = "undefined" ]; then + version="${chromeos_version}" fi local image_type From bfdf4a8913c805bd74723aa1daf9eeeaa971e590 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 14 Mar 2014 09:31:33 -0400 Subject: [PATCH 156/225] user_collector: use %P instead of %p The latter will give us the PID as seen in the current PID namespace, but since the crash handler always runs in the top most PID namespace, that is meaningless to us. Instead, use the %P option so we see the PID in the top most namespace. BUG=chromium:351568 TEST=`cbuildbot amd64-generic-full` passes # linux-3.10 TEST=`cbuildbot x86-alex-release` passes # linux-3.4 TEST=`cbuildbot link-release` passes # linux-3.8 CQ-DEPEND=CL:190011 CQ-DEPEND=CL:190020 CQ-DEPEND=CL:190012 CQ-DEPEND=CL:190021 Change-Id: Ic619c4e7514e2824f7ba31b73cb9e11ec4092774 Reviewed-on: https://chromium-review.googlesource.com/190010 Reviewed-by: Ben Chan Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/user_collector.cc | 2 +- crash_reporter/user_collector_test.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 4afad4356..f6841e623 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -107,7 +107,7 @@ std::string UserCollector::GetPattern(bool enabled) const { // the size of the invocation line for crash_reporter, since the kernel // has a fixed-sized (128B) buffer for it (before parameter expansion). // Note that the kernel does not support quoted arguments in core_pattern. - return StringPrintf("|%s --user=%%p:%%s:%%u:%%e", our_path_.c_str()); + return StringPrintf("|%s --user=%%P:%%s:%%u:%%e", our_path_.c_str()); } else { return "core"; } diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index d416a7bbd..eed6dafa0 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -71,7 +71,7 @@ class UserCollectorTest : public ::testing::Test { TEST_F(UserCollectorTest, EnableOK) { ASSERT_TRUE(collector_.Enable()); - ExpectFileEquals("|/my/path --user=%p:%s:%u:%e", "test/core_pattern"); + ExpectFileEquals("|/my/path --user=%P:%s:%u:%e", "test/core_pattern"); ExpectFileEquals("4", "test/core_pipe_limit"); ASSERT_EQ(s_crashes, 0); EXPECT_TRUE(FindLog("Enabling user crash handling")); From dffc8ced0ba11e911c8c5c01466e27e3ef8b915b Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Thu, 27 Mar 2014 12:26:20 -0700 Subject: [PATCH 157/225] crash-reporter: Update OWNERS file to set 'noparent'. BUG=None TEST=None Change-Id: I560e5247114f6be2c1689e8cdb636dc914332ef7 Reviewed-on: https://chromium-review.googlesource.com/191944 Reviewed-by: Mike Frysinger Commit-Queue: Ben Chan Tested-by: Ben Chan --- crash_reporter/OWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/crash_reporter/OWNERS b/crash_reporter/OWNERS index c5b51d0e6..5f9a4baef 100644 --- a/crash_reporter/OWNERS +++ b/crash_reporter/OWNERS @@ -1,2 +1,3 @@ +set noparent benchan@chromium.org vapier@chromium.org From 0446aab3b5742d0227f480eadbbffe42409e8f45 Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Wed, 26 Mar 2014 15:44:36 +0900 Subject: [PATCH 158/225] crash_collector: Disable kernel task state collection for chrome crashes Currently this results in outputting a lot of backtraces to system logs, making it hard to find other failures. Disable until a better way to do this is introduced. BUG=chromium:352834 TEST=P2_TEST_FILTER="crash-reporter::*" FEATURES=test emerge-link platform2 Change-Id: If6aa7c2d0d5fc9b0e0a902caacec84c2beb22b9c Reviewed-on: https://chromium-review.googlesource.com/191623 Reviewed-by: Pawel Osciak Tested-by: Pawel Osciak Commit-Queue: Pawel Osciak --- crash_reporter/chrome_collector.cc | 260 +---------------------------- 1 file changed, 2 insertions(+), 258 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index b5720975f..896d0c307 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -4,20 +4,15 @@ #include "crash-reporter/chrome_collector.h" -#include #include -#define __STDC_FORMAT_MACROS -#include #include #include #include #include #include -#include #include #include -#include #include "chromeos/process.h" #include "chromeos/syslog_logging.h" #include "chromeos/dbus/dbus.h" @@ -28,33 +23,6 @@ const char kTarPath[] = "/bin/tar"; // From //net/crash/collector/collector.h const int kDefaultMaxUploadBytes = 1024 * 1024; -const char kHeader[] = "kernel_task_states"; -const char kEnter[] = "enter"; -const char kLeave[] = "leave"; -const char kSyslogPath[] = "/var/log/messages"; -const char kFirstTwoColumnsRE[] = "\\S+\\s+\\S+\\s+"; -const char kIdent[] = "crash_reporter"; -const char kSpacesRE[] = "\\s+"; -const char kKernelIdentRE[] = "kernel:"; -const char kKernelTimestampRE[] = "\\[\\s*\\d+\\.\\d+\\]"; -const char kTaskNameRE[] = "(.{15})"; -// kernel's TASK_STATE_TO_CHAR_STR + '?' -const char kTaskStateRE[] = "([RSDTtZXxKW?])"; -const char kHexNumRE[] = "([[:xdigit:]]+)"; -const char kNumRE[] = "([[:digit:]]+)"; -const char kRunning32[] = "running "; -const char kRunning64[] = " running task "; -// This regex matches symbol+offset as printed by printk(%pB) or (%pS). -// See https://www.kernel.org/doc/Documentation/printk-formats.txt -// As noted in http://sourceware.org/binutils/docs-2.23.1/as/Symbol-Names.html -// symbols may contain periods and dollars, like "scm_recv.isra.18+0x61/0xdf". -// Strangely, occasionally there is an additional string following the symbol, -// like "qcusbnet_get+0x3e4/0x1e46 [gobi]". -// Those lines will be missing in the output, as parsing them is troublesome. -// Sometimes the address is printed and not the symbol. Those are ignored too. -const char kSymbolAndOffsetRE[] = - "([[:alpha:]._$][[:alnum:]._$]*)\\+0x([[:xdigit:]]+)/0x([[:xdigit:]]+)"; - using base::FilePath; using base::StringPrintf; @@ -118,216 +86,6 @@ bool GetDriErrorState(const chromeos::dbus::Proxy &proxy, return true; } -bool LogKernelTaskStates(const chromeos::dbus::Proxy &proxy, - const std::string &task_states_id) { - // Wrap task states with unique id - header - LOG(INFO) << kHeader << " " << task_states_id.c_str() << " " << kEnter; - - // Trigger asynchronous logging of task states - chromeos::glib::ScopedError error; - if (!dbus_g_proxy_call(proxy.gproxy(), debugd::kLogKernelTaskStates, - &chromeos::Resetter(&error).lvalue(), - G_TYPE_INVALID, G_TYPE_INVALID)) { - LOG(ERROR) << "Error performing D-Bus proxy call " - << "'" << debugd::kLogKernelTaskStates << "'" - << ": " << (error ? error->message : ""); - return false; - } - - // Give kernel a second to finish the asynchronous logging - sleep(1); - // Wrap task states with unique id - footer - LOG(INFO) << kHeader << " " << task_states_id.c_str() << " " << kLeave; - - return true; -} - -bool GetKernelTaskStates(const chromeos::dbus::Proxy &proxy, - std::vector *output) { - std::string task_states_id = StringPrintf("%016"PRIX64, base::RandUint64()); - - if (!LogKernelTaskStates(proxy, task_states_id)) - return false; - - std::ifstream syslog_stream(kSyslogPath, std::ios::in); - if (!syslog_stream.is_open()) { - LOG(ERROR) << "Couldn't open syslog for reading"; - return false; - } - - std::string ident_re = StringPrintf("%s\\[%d\\]:\\s+", kIdent, getpid()); - // 2013-07-05T11:52:55.530503-07:00 localhost crash_reporter[9349]: - // kernel_task_states 33F114E753768B70 enter - pcrecpp::RE enter_re(kFirstTwoColumnsRE + ident_re + kHeader + " " + - task_states_id + " " + kEnter); - // 2013-07-05T11:52:56.556177-07:00 localhost crash_reporter[9349]: - // kernel_task_states 33F114E753768B70 leave - pcrecpp::RE leave_re(kFirstTwoColumnsRE + ident_re + kHeader + " " + - task_states_id + " " + kLeave); - // 2013-06-21T21:18:14.855071+00:00 localhost kernel: [362142.027766] - // NSS SSL ThreadW S 804f8e28 0 5995 1016 0x00000000 - pcrecpp::RE task_header_re(std::string(kFirstTwoColumnsRE) + - kKernelIdentRE + kSpacesRE + - kKernelTimestampRE + kSpacesRE + - kTaskNameRE + kSpacesRE + - kTaskStateRE + kSpacesRE + - kHexNumRE + kSpacesRE + - kNumRE + kSpacesRE + - kNumRE + kSpacesRE + - kNumRE + kSpacesRE + - "0x" + kHexNumRE); - // 2013-06-21T21:18:14.815190+00:00 localhost kernel: [362142.016915] - // rsyslogd R running 0 340 1 0x00000000 - pcrecpp::RE task_header_run32_re(std::string(kFirstTwoColumnsRE) + - kKernelIdentRE + kSpacesRE + - kKernelTimestampRE + kSpacesRE + - kTaskNameRE + kSpacesRE + - kTaskStateRE + kSpacesRE + - kRunning32 + kSpacesRE + - kNumRE + kSpacesRE + - kNumRE + kSpacesRE + - kNumRE + kSpacesRE + - "0x" + kHexNumRE); - // 2013-06-21T14:11:18.718810-07:00 localhost kernel: [ 656.217932] - // kworker/2:1 R running task 0 54 2 0x00000000 - pcrecpp::RE task_header_run64_re(std::string(kFirstTwoColumnsRE) + - kKernelIdentRE + kSpacesRE + - kKernelTimestampRE + kSpacesRE + - kTaskNameRE + kSpacesRE + - kTaskStateRE + kSpacesRE + - kRunning64 + kSpacesRE + - kNumRE + kSpacesRE + - kNumRE + kSpacesRE + - kNumRE + kSpacesRE + - "0x" + kHexNumRE); - std::string address_re = std::string("\\[<") + kHexNumRE + ">\\]"; - std::string braced_sym_re = std::string("\\(") + kSymbolAndOffsetRE + "\\)"; - // 2013-07-05T11:53:20.141355-07:00 localhost kernel: [ 169.476749] - // [<8002fb74>] (do_wait+0x1a8/0x248) from [<80030c48>] (sys_wait4+0xbc/0xf8) - pcrecpp::RE arm_stack_re(std::string(kFirstTwoColumnsRE) + - kKernelIdentRE + kSpacesRE + - kKernelTimestampRE + kSpacesRE + - address_re + kSpacesRE + - braced_sym_re + kSpacesRE + - "from" + kSpacesRE + - address_re + kSpacesRE + - braced_sym_re); - // 2013-06-21T14:12:34.457315-07:00 localhost kernel: [ 731.965907] - // [] ? system_call_fastpath+0x16/0x1b - pcrecpp::RE x86_stack_re(std::string(kFirstTwoColumnsRE) + - kKernelIdentRE + kSpacesRE + - kKernelTimestampRE + kSpacesRE + - address_re + kSpacesRE + - "(\\?\\s+)?" + - kSymbolAndOffsetRE); - - bool enter_matched = false; - bool leave_matched = false; - uint64 prev_address = 0; - std::string line; - while (std::getline(syslog_stream, line)) { - if (leave_re.FullMatch(line)) { - if (!enter_matched) { - LOG(ERROR) << "Task states header not found before footer"; - return false; - } - leave_matched = true; - break; // Stop processing input after footer - } - - if (enter_re.FullMatch(line)) { - if (enter_matched) { - LOG(ERROR) << "Task states header encountered twice"; - return false; - } - enter_matched = true; - continue; - } - - if(!enter_matched) - continue; // Skip lines before header - - std::string task_name, task_state; - uint64 address1, address2; - unsigned long free_stack; - pid_t pid, ppid; - unsigned long flags; - std::string sym1, sym2; - uint64 offset1a, offset1b, offset2a, offset2b; - std::string question; - - if (task_header_re.FullMatch(line, &task_name, &task_state, - pcrecpp::Hex(&address1), &free_stack, &pid, - &ppid, pcrecpp::Hex(&flags))) { - prev_address = 0; - output->push_back(StringPrintf("%s %s %016"PRIx64" %5lu %5d %6d 0x%08lx", - task_name.c_str(), task_state.c_str(), - address1, free_stack, pid, ppid, flags)); - } else if (task_header_run32_re.FullMatch(line, &task_name, &task_state, - &free_stack, &pid, &ppid, - pcrecpp::Hex(&flags)) || - task_header_run64_re.FullMatch(line, &task_name, &task_state, - &free_stack, &pid, &ppid, - pcrecpp::Hex(&flags))) { - prev_address = 0; - output->push_back(StringPrintf("%s %s %s %5lu %5d %6d 0x%08lx", - task_name.c_str(), task_state.c_str(), - kRunning64, free_stack, pid, ppid, flags)); - } else if (arm_stack_re.FullMatch(line, pcrecpp::Hex(&address1), &sym1, - pcrecpp::Hex(&offset1a), - pcrecpp::Hex(&offset1b), - pcrecpp::Hex(&address2), &sym2, - pcrecpp::Hex(&offset2a), - pcrecpp::Hex(&offset2b))) { - if (prev_address != address1) { - output->push_back(StringPrintf("%s+0x%"PRIx64"/0x%"PRIx64, - sym1.c_str(), offset1a, offset1b)); - } - output->push_back(StringPrintf("%s+0x%"PRIx64"/0x%"PRIx64, - sym2.c_str(), offset2a, offset2b)); - prev_address = address2; - } else if (x86_stack_re.FullMatch(line, pcrecpp::Hex(&address1), &question, - &sym1, pcrecpp::Hex(&offset1a), - pcrecpp::Hex(&offset1b))) { - output->push_back(StringPrintf("%s%s+0x%"PRIx64"/0x%"PRIx64, - question.c_str(), sym1.c_str(), - offset1a, offset1b)); - } - } - syslog_stream.close(); - - if(!enter_matched || !leave_matched) { - LOG(ERROR) << "Task states header or footer not found"; - return false; - } - - return true; -} - -bool WriteKernelTaskStates(const chromeos::dbus::Proxy &proxy, - const FilePath &task_states_path) { - std::ofstream task_states_stream(task_states_path.value().c_str(), - std::ios::out); - if (!task_states_stream.is_open()) { - LOG(ERROR) << "Could not write file " << task_states_path.value(); - return false; - } - - std::vector task_sates; - if (!GetKernelTaskStates(proxy, &task_sates)) { - base::DeleteFile(task_states_path, false); - return false; - } - - for (std::vector::iterator it = task_sates.begin(); - it != task_sates.end(); ++it) { - task_states_stream << *it << std::endl; - } - task_states_stream.close(); - - return true; -} - bool GetAdditionalLogs(const FilePath &log_path) { chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); if (!dbus.HasConnection()) { @@ -347,17 +105,7 @@ bool GetAdditionalLogs(const FilePath &log_path) { FilePath error_state_path = log_path.DirName().Append("i915_error_state.log.xz"); - bool has_dri_error_state = false; - if (GetDriErrorState(proxy, error_state_path)) - has_dri_error_state = true; - - FilePath task_states_path = log_path.DirName().Append("task_states.log"); - bool has_task_states = false; - if (WriteKernelTaskStates(proxy, task_states_path)) { - has_task_states = true; - } - - if (!has_dri_error_state && !has_task_states) + if (!GetDriErrorState(proxy, error_state_path)) return false; chromeos::ProcessImpl tar_process; @@ -365,14 +113,10 @@ bool GetAdditionalLogs(const FilePath &log_path) { tar_process.AddArg("cfJ"); tar_process.AddArg(log_path.value()); tar_process.AddStringOption("-C", log_path.DirName().value()); - if (has_dri_error_state) - tar_process.AddArg(error_state_path.BaseName().value()); - if (has_task_states) - tar_process.AddArg(task_states_path.BaseName().value()); + tar_process.AddArg(error_state_path.BaseName().value()); int res = tar_process.Run(); base::DeleteFile(error_state_path, false); - base::DeleteFile(task_states_path, false); if (res || !base::PathExists(log_path)) { LOG(ERROR) << "Could not tar file " << log_path.value(); From 042c3d406a970100dfd024dc10bc5af3e030b297 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 8 Apr 2014 14:34:04 -0400 Subject: [PATCH 159/225] crash_sender: log product info in new list format The 00A0 unicode space hack hasn't been reliable. The latest Chromium code supports a 3rd field in the crash list though, so let's use that instead. The UI has already been updated to look for this: https://codereview.chromium.org/219383008/ BUG=chromium:210624 TEST=logged a system crash and looked at crashes page TEST=logged a chrome crash and looked at crashes page TEST=`cbuildbot {x86,amd64}-generic-full` pass (VMTests) Change-Id: Icce0d9d26276143a1604b8eef5c68a2a6417c912 Reviewed-on: https://chromium-review.googlesource.com/193611 Reviewed-by: Mike Frysinger Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_sender | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 3b4f18361..9dcf34631 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -460,14 +460,22 @@ send_crash() { local product_name local timestamp="$(date +%s)" case ${product} in - Chrome_ChromeOS) + Chrome_ChromeOS) + if is_official_image; then product_name="Chrome" - ;; - *) + else + product_name="Chromium" + fi + ;; + *) + if is_official_image; then product_name="ChromeOS" - ;; + else + product_name="ChromiumOS" + fi + ;; esac - /usr/bin/printf '%s,%s\u00A0(%s)\n' \ + printf '%s,%s,%s\n' \ "${timestamp}" "${id}" "${product_name}" >> "${CHROME_CRASH_LOG}" lecho "Crash report receipt ID ${id}" else From ebad658b4140ac20fe0d3b71d4cb5c64c7640865 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Thu, 10 Apr 2014 11:27:22 -0700 Subject: [PATCH 160/225] Add crash reporter's init scripts Part of a three CLs commit that moves crash reporter scripts out of platform/init. BUG=chromium:346449 TEST=none CQ-DEPEND=Ib74673a38ef3886b9b23e2f152dd796a3e29855a CQ-DEPEND=I079759d0a096c50600c292cc37d3486b1c44e56a Change-Id: Icaad09443e60ff8dd4527d426cc11204a65c5f96 Reviewed-on: https://chromium-review.googlesource.com/194298 Reviewed-by: Mike Frysinger Tested-by: Bertrand Simonnet Commit-Queue: Bertrand Simonnet --- crash_reporter/init/crash-reporter.conf | 21 +++++++++++++++++++++ crash_reporter/init/warn-collector.conf | 12 ++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 crash_reporter/init/crash-reporter.conf create mode 100644 crash_reporter/init/warn-collector.conf diff --git a/crash_reporter/init/crash-reporter.conf b/crash_reporter/init/crash-reporter.conf new file mode 100644 index 000000000..3463c60c7 --- /dev/null +++ b/crash_reporter/init/crash-reporter.conf @@ -0,0 +1,21 @@ +# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +description "Initialize crash reporting services" +author "chromium-os-dev@chromium.org" + +# This job merely initializes its service and then terminates; the +# actual checking and reporting of crash dumps is triggered by an +# hourly cron job. +start on starting system-services + +pre-start script + mkdir -p /var/spool +end script + +# crash_reporter uses argv[0] as part of the command line for +# /proc/sys/kernel/core_pattern. That command line is invoked by +# the kernel, and can't rely on PATH, so argv[0] must be a full +# path; we invoke it as such here. +exec /sbin/crash_reporter --init diff --git a/crash_reporter/init/warn-collector.conf b/crash_reporter/init/warn-collector.conf new file mode 100644 index 000000000..3be80daab --- /dev/null +++ b/crash_reporter/init/warn-collector.conf @@ -0,0 +1,12 @@ +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +description "Runs a daemon which collects and reports kernel warnings" +author "chromium-os-dev@chromium.org" + +start on started system-services +stop on stopping system-services +respawn + +exec warn_collector From 553e28c233ce178d7878e705101dde01e789497d Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Wed, 23 Apr 2014 22:10:54 -0700 Subject: [PATCH 161/225] crash-reporter: Remove libbase_ver override in gyp file. CL:194476 modified platform2 to build against libchrome-242728. The libbase_ver override in the gyp file is no longer needed. BUG=chromium:361748 TEST=`FEATURES=test emerge-$BOARD platform2` TEST=Verify that crash-reporter is linked against libchrome-242728. Change-Id: I46a362165e08839bce82748f55f8f09595b8ac18 Reviewed-on: https://chromium-review.googlesource.com/196725 Commit-Queue: Ben Chan Tested-by: Ben Chan Reviewed-by: Mike Frysinger --- crash_reporter/crash-reporter.gyp | 3 --- 1 file changed, 3 deletions(-) diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index 0b9d9b109..daa1ed0e7 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -7,9 +7,6 @@ ], }, }, - 'variables': { - 'libbase_ver': 242728, - }, 'targets': [ { 'target_name': 'libcrash', From 9b1f300139689eb9c7b7a35e91a4fbc1eab93b4e Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Thu, 24 Apr 2014 02:10:57 -0700 Subject: [PATCH 162/225] crash-reporter: Write out a magic string for Chrome crashes. Also do some much needed code cleanup. BUG=chromium:363660 TEST=emerge platform2 Change-Id: Ica9abfd854e2c77d970851805989c86a6a45fdee Reviewed-on: https://chromium-review.googlesource.com/196764 Commit-Queue: Lei Zhang Tested-by: Lei Zhang Reviewed-by: Mike Frysinger --- crash_reporter/chrome_collector.cc | 14 +++-- crash_reporter/chrome_collector.h | 12 ++++- crash_reporter/chrome_collector_test.cc | 69 +++++++++++++++---------- crash_reporter/crash_collector.cc | 9 ++-- crash_reporter/crash_collector.h | 10 ++-- crash_reporter/crash_collector_test.cc | 5 +- crash_reporter/crash_reporter.cc | 7 ++- crash_reporter/kernel_collector_test.cc | 64 ++++++++++++----------- crash_reporter/udev_collector_test.cc | 45 ++++++++-------- crash_reporter/user_collector_test.cc | 12 ++--- 10 files changed, 141 insertions(+), 106 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 896d0c307..9798ec538 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -128,11 +128,11 @@ bool GetAdditionalLogs(const FilePath &log_path) { } //namespace -ChromeCollector::ChromeCollector() {} +ChromeCollector::ChromeCollector() : output_file_ptr_(stdout) {} ChromeCollector::~ChromeCollector() {} -bool ChromeCollector::HandleCrash(const std::string &file_path, +bool ChromeCollector::HandleCrash(const FilePath &file_path, const std::string &pid_string, const std::string &uid_string, const std::string &exe_name) { @@ -158,8 +158,8 @@ bool ChromeCollector::HandleCrash(const std::string &file_path, FilePath log_path = GetCrashPath(dir, dump_basename, "log.tar.xz"); std::string data; - if (!base::ReadFileToString(FilePath(file_path), &data)) { - LOG(ERROR) << "Can't read crash log: " << file_path.c_str(); + if (!base::ReadFileToString(file_path, &data)) { + LOG(ERROR) << "Can't read crash log: " << file_path.value(); return false; } @@ -186,6 +186,9 @@ bool ChromeCollector::HandleCrash(const std::string &file_path, // We're done. WriteCrashMetaData(meta_path, exe_name, minidump_path.value()); + fprintf(output_file_ptr_, "%s", kSuccessMagic); + fflush(output_file_ptr_); + return true; } @@ -288,3 +291,6 @@ bool ChromeCollector::ParseCrashLog(const std::string &data, return at == data.size(); } + +// static +const char ChromeCollector::kSuccessMagic[] = "_sys_cr_finished"; diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h index ab594071f..c6dbfc944 100644 --- a/crash_reporter/chrome_collector.h +++ b/crash_reporter/chrome_collector.h @@ -19,9 +19,14 @@ class ChromeCollector : public CrashCollector { ChromeCollector(); virtual ~ChromeCollector(); + // Magic string to let Chrome know the crash report succeeded. + static const char kSuccessMagic[]; + // Handle a specific chrome crash. Returns true on success. - bool HandleCrash(const std::string &file_path, const std::string &pid_string, - const std::string &uid_string, const std::string &exe_name); + bool HandleCrash(const base::FilePath &file_path, + const std::string &pid_string, + const std::string &uid_string, + const std::string &exe_name); private: friend class ChromeCollectorTest; @@ -29,6 +34,7 @@ class ChromeCollector : public CrashCollector { FRIEND_TEST(ChromeCollectorTest, BadValues); FRIEND_TEST(ChromeCollectorTest, Newlines); FRIEND_TEST(ChromeCollectorTest, File); + FRIEND_TEST(ChromeCollectorTest, HandleCrash); // Crashes are expected to be in a TLV-style format of: // :: @@ -39,6 +45,8 @@ class ChromeCollector : public CrashCollector { bool ParseCrashLog(const std::string &data, const base::FilePath &dir, const base::FilePath &minidump, const std::string &basename); + + FILE *output_file_ptr_; }; #endif diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc index 8a82cb210..37a180bfa 100644 --- a/crash_reporter/chrome_collector_test.cc +++ b/crash_reporter/chrome_collector_test.cc @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include -#include -#include +#include +#include + +#include "base/auto_reset.h" #include "base/file_util.h" #include "base/files/scoped_temp_dir.h" -#include "base/strings/string_split.h" #include "chromeos/syslog_logging.h" #include "chromeos/test_helpers.h" #include "crash-reporter/chrome_collector.h" @@ -30,37 +30,30 @@ static const char kCrashFormatWithFile[] = "value3:2:ok"; void CountCrash() { - static int s_crashes = 0; - ++s_crashes; } +static bool s_allow_crash = false; + bool IsMetrics() { - return false; + return s_allow_crash; } class ChromeCollectorTest : public ::testing::Test { - void SetUp() { - collector_.Initialize(CountCrash, IsMetrics); - pid_ = getpid(); - chromeos::ClearLog(); - } - protected: void ExpectFileEquals(const char *golden, - const char *file_path) { + const FilePath &file_path) { std::string contents; - EXPECT_TRUE(base::ReadFileToString(FilePath(file_path), &contents)); + EXPECT_TRUE(base::ReadFileToString(file_path, &contents)); EXPECT_EQ(golden, contents); } - std::vector SplitLines(const std::string &lines) const { - std::vector result; - base::SplitString(lines, '\n', &result); - return result; - } - ChromeCollector collector_; - pid_t pid_; + + private: + void SetUp() OVERRIDE { + collector_.Initialize(CountCrash, IsMetrics); + chromeos::ClearLog(); + } }; TEST_F(ChromeCollectorTest, GoodValues) { @@ -107,7 +100,9 @@ TEST_F(ChromeCollectorTest, BadValues) { } TEST_F(ChromeCollectorTest, File) { - FilePath dir("."); + base::ScopedTempDir scoped_temp_dir; + ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); + const FilePath& dir = scoped_temp_dir.path(); EXPECT_TRUE(collector_.ParseCrashLog(kCrashFormatWithFile, dir, dir.Append("minidump.dmp"), "base")); @@ -118,12 +113,34 @@ TEST_F(ChromeCollectorTest, File) { EXPECT_TRUE(meta.find("value1=abcdefghij") != std::string::npos); EXPECT_TRUE(meta.find("value2=12345") != std::string::npos); EXPECT_TRUE(meta.find("value3=ok") != std::string::npos); - ExpectFileEquals("12345\n789\n12345", - dir.Append("base-foo.txt.other").value().c_str()); - base::DeleteFile(dir.Append("base-foo.txt.other"), false); + ExpectFileEquals("12345\n789\n12345", dir.Append("base-foo.txt.other")); +} + +TEST_F(ChromeCollectorTest, HandleCrash) { + base::AutoReset auto_reset(&s_allow_crash, true); + base::ScopedTempDir scoped_temp_dir; + ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); + const FilePath& dir = scoped_temp_dir.path(); + FilePath dump_file = dir.Append("test.dmp"); + ASSERT_EQ(strlen(kCrashFormatWithFile), + file_util::WriteFile(dump_file, kCrashFormatWithFile, + strlen(kCrashFormatWithFile))); + collector_.ForceCrashDirectory(dir); + + FilePath log_file; + { + file_util::ScopedFILE output( + base::CreateAndOpenTemporaryFileInDir(dir, &log_file)); + ASSERT_TRUE(output.get()); + base::AutoReset auto_reset_file_ptr(&collector_.output_file_ptr_, + output.get()); + EXPECT_TRUE(collector_.HandleCrash(dump_file, "123", "456", "chrome_test")); + } + ExpectFileEquals(ChromeCollector::kSuccessMagic, log_file); } int main(int argc, char **argv) { + ::g_type_init(); SetUpTests(&argc, argv, false); return RUN_ALL_TESTS(); } diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 0521a0645..1a0898b3c 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -76,8 +76,7 @@ using base::FilePath; using base::StringPrintf; CrashCollector::CrashCollector() - : forced_crash_directory_(NULL), - lsb_release_(kLsbRelease), + : lsb_release_(kLsbRelease), log_config_path_(kDefaultLogConfig) { } @@ -262,8 +261,8 @@ bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid, if (out_of_capacity != NULL) *out_of_capacity = false; // For testing. - if (forced_crash_directory_ != NULL) { - *crash_directory = FilePath(forced_crash_directory_); + if (!forced_crash_directory_.empty()) { + *crash_directory = forced_crash_directory_; return true; } @@ -498,7 +497,7 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, const std::string &exec_name, const std::string &payload_path) { std::map contents; - if (!ReadKeyValueFile(FilePath(std::string(lsb_release_)), '=', &contents)) { + if (!ReadKeyValueFile(FilePath(lsb_release_), '=', &contents)) { LOG(ERROR) << "Problem parsing " << lsb_release_; // Even though there was some failure, take as much as we could read. } diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index f1cf32e27..67ff87089 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -32,6 +32,7 @@ class CrashCollector { protected: friend class CrashCollectorTest; + FRIEND_TEST(ChromeCollectorTest, HandleCrash); FRIEND_TEST(CrashCollectorTest, CheckHasCapacityCorrectBasename); FRIEND_TEST(CrashCollectorTest, CheckHasCapacityStrangeNames); FRIEND_TEST(CrashCollectorTest, CheckHasCapacityUsual); @@ -71,7 +72,7 @@ class CrashCollector { // For testing, set the directory always returned by // GetCreatedCrashDirectoryByEuid. - void ForceCrashDirectory(const char *forced_directory) { + void ForceCrashDirectory(const base::FilePath &forced_directory) { forced_crash_directory_ = forced_directory; } @@ -86,7 +87,8 @@ class CrashCollector { bool GetUserInfoFromName(const std::string &name, uid_t *uid, gid_t *gid); - // Determines the crash directory for given eud, and creates the + + // Determines the crash directory for given euid, and creates the // directory if necessary with appropriate permissions. If // |out_of_capacity| is not NULL, it is set to indicate if the call // failed due to not having capacity in the crash directory. Returns @@ -165,8 +167,8 @@ class CrashCollector { CountCrashFunction count_crash_function_; IsFeedbackAllowedFunction is_feedback_allowed_function_; std::string extra_metadata_; - const char *forced_crash_directory_; - const char *lsb_release_; + base::FilePath forced_crash_directory_; + std::string lsb_release_; base::FilePath log_config_path_; }; diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 7d3c1fe9f..863fed3f4 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -34,8 +34,7 @@ bool IsMetrics() { class CrashCollectorTest : public ::testing::Test { public: void SetUp() { - collector_.Initialize(CountCrash, - IsMetrics); + collector_.Initialize(CountCrash, IsMetrics); test_dir_ = FilePath("test"); base::CreateDirectory(test_dir_); chromeos::ClearLog(); @@ -281,7 +280,7 @@ TEST_F(CrashCollectorTest, MetaData) { FilePath lsb_release = test_dir_.Append("lsb-release"); FilePath payload_file = test_dir_.Append("payload-file"); std::string contents; - collector_.lsb_release_ = lsb_release.value().c_str(); + collector_.lsb_release_ = lsb_release.value(); const char kLsbContents[] = "CHROMEOS_RELEASE_VERSION=version\n"; ASSERT_TRUE( file_util::WriteFile(lsb_release, diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 1da1d8741..828ec9194 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -186,8 +186,8 @@ static int HandleChromeCrash(ChromeCollector *chrome_collector) { CHECK(!FLAGS_exe.empty()) << "--exe= must be set"; chromeos::LogToString(true); - bool handled = chrome_collector->HandleCrash(FLAGS_chrome, FLAGS_pid, - FLAGS_uid, FLAGS_exe); + bool handled = chrome_collector->HandleCrash(FilePath(FLAGS_chrome), + FLAGS_pid, FLAGS_uid, FLAGS_exe); chromeos::LogToString(false); if (!handled) return 1; @@ -269,8 +269,7 @@ int main(int argc, char *argv[]) { ::g_type_init(); KernelCollector kernel_collector; - kernel_collector.Initialize(CountKernelCrash, - IsFeedbackAllowed); + kernel_collector.Initialize(CountKernelCrash, IsFeedbackAllowed); UserCollector user_collector; user_collector.Initialize(CountUserCrash, my_path.value(), diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index dd6e5ba62..14b8357fd 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -5,6 +5,7 @@ #include #include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "chromeos/syslog_logging.h" @@ -15,9 +16,6 @@ static int s_crashes = 0; static bool s_metrics = false; -static const char kTestKCrash[] = "test/kcrash"; -static const char kTestCrashDirectory[] = "test/crash_directory"; - using base::FilePath; using base::StringPrintf; using chromeos::FindLog; @@ -32,24 +30,6 @@ bool IsMetrics() { } class KernelCollectorTest : public ::testing::Test { - void SetUp() { - s_crashes = 0; - s_metrics = true; - collector_.Initialize(CountCrash, - IsMetrics); - mkdir("test", 0777); - mkdir(kTestKCrash, 0777); - test_kcrash_ = FilePath(kTestKCrash); - collector_.OverridePreservedDumpPath(test_kcrash_); - test_kcrash_ = test_kcrash_.Append("dmesg-ramoops-0"); - unlink(test_kcrash_.value().c_str()); - if (mkdir(kTestCrashDirectory, 0777)) { - ASSERT_EQ(EEXIST, errno) - << "Error while creating directory '" << kTestCrashDirectory - << "': " << strerror(errno); - } - chromeos::ClearLog(); - } protected: void WriteStringToFile(const FilePath &file_path, const char *data) { @@ -60,8 +40,32 @@ class KernelCollectorTest : public ::testing::Test { void SetUpSuccessfulCollect(); void ComputeKernelStackSignatureCommon(); + const FilePath &kcrash_file() const { return test_kcrash_; } + const FilePath &test_crash_directory() const { return test_crash_directory_; } + KernelCollector collector_; + + private: + void SetUp() OVERRIDE { + s_crashes = 0; + s_metrics = true; + collector_.Initialize(CountCrash, IsMetrics); + ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); + test_kcrash_ = scoped_temp_dir_.path().Append("kcrash"); + ASSERT_TRUE(base::CreateDirectory(test_kcrash_)); + collector_.OverridePreservedDumpPath(test_kcrash_); + + test_kcrash_ = test_kcrash_.Append("dmesg-ramoops-0"); + ASSERT_FALSE(base::PathExists(test_kcrash_)); + + test_crash_directory_ = scoped_temp_dir_.path().Append("crash_directory"); + ASSERT_TRUE(base::CreateDirectory(test_crash_directory_)); + chromeos::ClearLog(); + } + FilePath test_kcrash_; + FilePath test_crash_directory_; + base::ScopedTempDir scoped_temp_dir_; }; TEST_F(KernelCollectorTest, ComputeKernelStackSignatureBase) { @@ -70,16 +74,16 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignatureBase) { } TEST_F(KernelCollectorTest, LoadPreservedDump) { - ASSERT_FALSE(base::PathExists(test_kcrash_)); + ASSERT_FALSE(base::PathExists(kcrash_file())); std::string dump; dump.clear(); - WriteStringToFile(test_kcrash_, "emptydata"); + WriteStringToFile(kcrash_file(), "emptydata"); ASSERT_TRUE(collector_.LoadParameters()); ASSERT_FALSE(collector_.LoadPreservedDump(&dump)); ASSERT_EQ("", dump); - WriteStringToFile(test_kcrash_, "====1.1\nsomething"); + WriteStringToFile(kcrash_file(), "====1.1\nsomething"); ASSERT_TRUE(collector_.LoadParameters()); ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); ASSERT_EQ("something", dump); @@ -94,7 +98,7 @@ TEST_F(KernelCollectorTest, EnableMissingKernel) { } TEST_F(KernelCollectorTest, EnableOK) { - WriteStringToFile(test_kcrash_, ""); + WriteStringToFile(kcrash_file(), ""); ASSERT_TRUE(collector_.Enable()); ASSERT_TRUE(collector_.IsEnabled()); ASSERT_TRUE(FindLog("Enabling kernel crash handling")); @@ -225,7 +229,7 @@ TEST_F(KernelCollectorTest, CollectPreservedFileMissing) { } TEST_F(KernelCollectorTest, CollectNoCrash) { - WriteStringToFile(test_kcrash_, ""); + WriteStringToFile(kcrash_file(), ""); ASSERT_FALSE(collector_.Collect()); ASSERT_TRUE(FindLog("No valid records found")); ASSERT_FALSE(FindLog("Stored kcrash to ")); @@ -233,7 +237,7 @@ TEST_F(KernelCollectorTest, CollectNoCrash) { } TEST_F(KernelCollectorTest, CollectBadDirectory) { - WriteStringToFile(test_kcrash_, "====1.1\nsomething"); + WriteStringToFile(kcrash_file(), "====1.1\nsomething"); ASSERT_TRUE(collector_.Collect()); ASSERT_TRUE(FindLog("Unable to create appropriate crash directory")) << "Did not find expected error string in log: {\n" @@ -242,8 +246,8 @@ TEST_F(KernelCollectorTest, CollectBadDirectory) { } void KernelCollectorTest::SetUpSuccessfulCollect() { - collector_.ForceCrashDirectory(kTestCrashDirectory); - WriteStringToFile(test_kcrash_, "====1.1\nsomething"); + collector_.ForceCrashDirectory(test_crash_directory()); + WriteStringToFile(kcrash_file(), "====1.1\nsomething"); ASSERT_EQ(0, s_crashes); } @@ -272,7 +276,7 @@ TEST_F(KernelCollectorTest, CollectOK) { size_t end_pos = filename.find_first_of("\n"); ASSERT_NE(std::string::npos, end_pos); filename = filename.substr(0, end_pos); - ASSERT_EQ(0, filename.find(kTestCrashDirectory)); + ASSERT_EQ(0, filename.find(test_crash_directory().value())); ASSERT_TRUE(base::PathExists(FilePath(filename))); std::string contents; ASSERT_TRUE(base::ReadFileToString(FilePath(filename), &contents)); diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc index 6a5c8569e..db1550fd6 100644 --- a/crash_reporter/udev_collector_test.cc +++ b/crash_reporter/udev_collector_test.cc @@ -5,7 +5,6 @@ #include "base/file_util.h" #include "base/files/file_enumerator.h" #include "base/files/scoped_temp_dir.h" -#include "base/memory/scoped_ptr.h" #include "chromeos/test_helpers.h" #include "crash-reporter/udev_collector.h" #include "gtest/gtest.h" @@ -47,21 +46,25 @@ int GetNumLogFiles(const FilePath& path) { } // namespace class UdevCollectorTest : public ::testing::Test { - void SetUp() { + protected: + base::ScopedTempDir temp_dir_generator_; + + void HandleCrash(const std::string &udev_event) { + collector_.HandleCrash(udev_event); + } + + private: + void SetUp() OVERRIDE { s_consent_given = true; - collector_.reset(new UdevCollector); - collector_->Initialize(CountCrash, IsMetrics); + collector_.Initialize(CountCrash, IsMetrics); - temp_dir_generator_.reset(new base::ScopedTempDir()); - ASSERT_TRUE(temp_dir_generator_->CreateUniqueTempDir()); - EXPECT_TRUE(temp_dir_generator_->IsValid()); + ASSERT_TRUE(temp_dir_generator_.CreateUniqueTempDir()); FilePath log_config_path = - temp_dir_generator_->path().Append(kLogConfigFileName); - collector_->log_config_path_ = log_config_path; - collector_->ForceCrashDirectory( - temp_dir_generator_->path().value().c_str()); + temp_dir_generator_.path().Append(kLogConfigFileName); + collector_.log_config_path_ = log_config_path; + collector_.ForceCrashDirectory(temp_dir_generator_.path()); // Write to a dummy log config file. ASSERT_EQ(strlen(kLogConfigFileContents), @@ -72,30 +75,28 @@ class UdevCollectorTest : public ::testing::Test { chromeos::ClearLog(); } - protected: - scoped_ptr collector_; - scoped_ptr temp_dir_generator_; + UdevCollector collector_; }; TEST_F(UdevCollectorTest, TestNoConsent) { s_consent_given = false; - collector_->HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm"); - EXPECT_EQ(0, GetNumLogFiles(temp_dir_generator_->path())); + HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm"); + EXPECT_EQ(0, GetNumLogFiles(temp_dir_generator_.path())); } TEST_F(UdevCollectorTest, TestNoMatch) { // No rule should match this. - collector_->HandleCrash("ACTION=change:KERNEL=foo:SUBSYSTEM=bar"); - EXPECT_EQ(0, GetNumLogFiles(temp_dir_generator_->path())); + HandleCrash("ACTION=change:KERNEL=foo:SUBSYSTEM=bar"); + EXPECT_EQ(0, GetNumLogFiles(temp_dir_generator_.path())); } TEST_F(UdevCollectorTest, TestMatches) { // Try multiple udev events in sequence. The number of log files generated // should increase. - collector_->HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm"); - EXPECT_EQ(1, GetNumLogFiles(temp_dir_generator_->path())); - collector_->HandleCrash("ACTION=add:KERNEL=state0:SUBSYSTEM=cpu"); - EXPECT_EQ(2, GetNumLogFiles(temp_dir_generator_->path())); + HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm"); + EXPECT_EQ(1, GetNumLogFiles(temp_dir_generator_.path())); + HandleCrash("ACTION=add:KERNEL=state0:SUBSYSTEM=cpu"); + EXPECT_EQ(2, GetNumLogFiles(temp_dir_generator_.path())); } // TODO(sque, crosbug.com/32238) - test wildcard cases, multiple identical udev diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index eed6dafa0..0dd35e0d4 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -52,10 +52,9 @@ class UserCollectorTest : public ::testing::Test { protected: void ExpectFileEquals(const char *golden, - const char *file_path) { + const FilePath &file_path) { std::string contents; - EXPECT_TRUE(base::ReadFileToString(FilePath(file_path), - &contents)); + EXPECT_TRUE(base::ReadFileToString(file_path, &contents)); EXPECT_EQ(golden, contents); } @@ -71,8 +70,9 @@ class UserCollectorTest : public ::testing::Test { TEST_F(UserCollectorTest, EnableOK) { ASSERT_TRUE(collector_.Enable()); - ExpectFileEquals("|/my/path --user=%P:%s:%u:%e", "test/core_pattern"); - ExpectFileEquals("4", "test/core_pipe_limit"); + ExpectFileEquals("|/my/path --user=%P:%s:%u:%e", + FilePath("test/core_pattern")); + ExpectFileEquals("4", FilePath("test/core_pipe_limit")); ASSERT_EQ(s_crashes, 0); EXPECT_TRUE(FindLog("Enabling user crash handling")); } @@ -98,7 +98,7 @@ TEST_F(UserCollectorTest, EnableNoPipeLimitFileAccess) { TEST_F(UserCollectorTest, DisableOK) { ASSERT_TRUE(collector_.Disable()); - ExpectFileEquals("core", "test/core_pattern"); + ExpectFileEquals("core", FilePath("test/core_pattern")); ASSERT_EQ(s_crashes, 0); EXPECT_TRUE(FindLog("Disabling user crash handling")); } From f9f7807c88caef1fd93280375cd3322281d185ed Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Tue, 6 May 2014 14:08:27 -0700 Subject: [PATCH 163/225] Add crash-sender's init script to crash-reporter Part of a three CLs commit that moves crash-sender.conf from platform/init to platform/crash-reporter. BUG=chromium:346449 TEST=build chromeos-init and platform2. crash-sender.conf is now owned by platform2. CQ-DEPEND=If4ed3501dc7764515eee5284b608859b97652625 CQ-DEPEND=I1f536361e4089a38c70ee9b77f126b1b9881abd8 Change-Id: I7e8da8b755f531bf1987c369692cf570eb7a5548 Reviewed-on: https://chromium-review.googlesource.com/198602 Reviewed-by: Gaurav Shah Reviewed-by: Mike Frysinger Commit-Queue: Bertrand Simonnet Tested-by: Bertrand Simonnet --- crash_reporter/init/crash-sender.conf | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 crash_reporter/init/crash-sender.conf diff --git a/crash_reporter/init/crash-sender.conf b/crash_reporter/init/crash-sender.conf new file mode 100644 index 000000000..25ad2f8c8 --- /dev/null +++ b/crash_reporter/init/crash-sender.conf @@ -0,0 +1,11 @@ +# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +description "Run the crash sender periodically" +author "chromium-os-dev@chromium.org" + +start on starting system-services +stop on stopping system-services + +exec periodic_scheduler 3600 crash_sender /sbin/crash_sender From 618b579f4f1765e8cc506fbcebed60148b099857 Mon Sep 17 00:00:00 2001 From: Thiemo Nagel Date: Thu, 8 May 2014 19:53:53 +0200 Subject: [PATCH 164/225] crash_sender: include verbose curl output in error logs BUG=None TEST=Manual TEST=`cbuildbot {arm,amd64,x86}-generic-full` pass Change-Id: I7ee8f98aa1721d50bb8b86fd84b50d9432eaaa9d Reviewed-on: https://chromium-review.googlesource.com/198682 Reviewed-by: Mike Frysinger Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_sender | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 9dcf34631..565d949dd 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -437,7 +437,7 @@ send_crash() { local curl_stderr="${TMP_DIR}/curl_stderr" set +e - curl "${url}" ${proxy:+--proxy "$proxy"} \ + curl "${url}" -v ${proxy:+--proxy "$proxy"} \ --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \ -F "prod=${product}" \ -F "ver=${version}" \ @@ -456,7 +456,7 @@ send_crash() { set -e if [ ${curl_result} -eq 0 ]; then - local id="$(cat ${report_id})" + local id="$(cat "${report_id}")" local product_name local timestamp="$(date +%s)" case ${product} in @@ -479,7 +479,8 @@ send_crash() { "${timestamp}" "${id}" "${product_name}" >> "${CHROME_CRASH_LOG}" lecho "Crash report receipt ID ${id}" else - lecho "Crash sending failed with: $(cat ${curl_stderr})" + lecho "Crash sending failed with exit code ${curl_result}: " \ + "$(cat "${curl_stderr}")" fi rm -f "${report_id}" From 8fce2859059b27147ddd15f9a0e89a2816ed12ca Mon Sep 17 00:00:00 2001 From: Thiemo Nagel Date: Fri, 9 May 2014 14:48:45 +0200 Subject: [PATCH 165/225] UserCollector: only create meta file if payload write succeeds This avoids creating meta files that are referencing a non-existant payload which can block crash sending. BUG=chromium:338977 TEST=Running unit tests locally did succeed. Change-Id: Ia7039e679150490571d57a0960e984590905ee52 Reviewed-on: https://chromium-review.googlesource.com/199060 Reviewed-by: Mike Frysinger Tested-by: Mike Frysinger Commit-Queue: Mike Frysinger --- crash_reporter/user_collector.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index f6841e623..78f548254 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -192,6 +192,8 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid, LOG(ERROR) << "Could not even get log directory; out of space?"; return; } + AddCrashMetaData("sig", kCollectionErrorSignature); + AddCrashMetaData("error_type", GetErrorTypeSignature(error_type)); std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); std::string error_log = chromeos::GetLog(); FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog"); @@ -210,9 +212,10 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid, // We must use WriteNewFile instead of file_util::WriteFile as we do // not want to write with root access to a symlink that an attacker // might have created. - WriteNewFile(log_path, error_log.data(), error_log.length()); - AddCrashMetaData("sig", kCollectionErrorSignature); - AddCrashMetaData("error_type", GetErrorTypeSignature(error_type)); + if (WriteNewFile(log_path, error_log.data(), error_log.length()) < 0) { + LOG(ERROR) << "Error writing new file " << log_path.value(); + return; + } WriteCrashMetaData(meta_path, exec, log_path.value()); } From f1a5014637180457af21cc40a5ca64ab18a6947b Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 14 May 2014 16:05:09 -0400 Subject: [PATCH 166/225] use IGNORE_EINTR w/close HANDLE_EINTR is both not safe and not useful on Linux systems. Switch to IGNORE_EINTR like Chromium has done everywhere. See http://crbug.com/269623 for details. BUG=chromium:373154 TEST=`cbuildbot {arm,amd64,x86}-generic-full` passes Change-Id: Ib7b5c6913a63cc391005e9814200b87ed7ed7733 Reviewed-on: https://chromium-review.googlesource.com/199850 Reviewed-by: Ben Chan Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_collector.cc | 2 +- crash_reporter/user_collector.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 1a0898b3c..89d9304d0 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -103,7 +103,7 @@ int CrashCollector::WriteNewFile(const FilePath &filename, } int rv = file_util::WriteFileDescriptor(fd, data, size); - HANDLE_EINTR(close(fd)); + IGNORE_EINTR(close(fd)); return rv; } diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 78f548254..5b0bdb357 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -272,7 +272,7 @@ UserCollector::ErrorType UserCollector::ValidateCoreFile( char e_ident[EI_NIDENT]; bool read_ok = base::ReadFromFD(fd, e_ident, sizeof(e_ident)); - HANDLE_EINTR(close(fd)); + IGNORE_EINTR(close(fd)); if (!read_ok) { LOG(ERROR) << "Could not read header of core file"; return kErrorInvalidCoreFile; From 9895096f8b8046456bec96292f9c547b11913248 Mon Sep 17 00:00:00 2001 From: Thiemo Nagel Date: Tue, 13 May 2014 19:48:32 +0200 Subject: [PATCH 167/225] crash_sender: fix orphan logic, prevent deletion of *.log.tar.xz get_base() was broken for files with multiple extensions which leads the orphan logic to delete all such files that are older than 1 day. This patch set fixes get_base(). BUG=chromium:338977 TEST=manual test of code fragment TEST=`cbuildbot {arm,amd64,x86}-generic-full` pass CQ-DEPEND=CL:199820 Change-Id: I1b712f1a5cfc848e5bc2a86c97e0a3e88ce6250d Reviewed-on: https://chromium-review.googlesource.com/199511 Reviewed-by: Ben Chan Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_collector.cc | 2 ++ crash_reporter/crash_sender | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 89d9304d0..5ddff085c 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -108,6 +108,8 @@ int CrashCollector::WriteNewFile(const FilePath &filename, } std::string CrashCollector::Sanitize(const std::string &name) { + // Make sure the sanitized name does not include any periods. + // The logic in crash_sender relies on this. std::string result = name; for (size_t i = 0; i < name.size(); ++i) { if (!isalnum(result[i]) && result[i] != '_') diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 565d949dd..e42cb023d 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -212,10 +212,11 @@ check_rate() { return 0 } -# Gets the base part of a crash report file, such as -# name.01234.5678.9012 from name.01234.5678.9012.meta +# Gets the base part of a crash report file, such as name.01234.5678.9012 from +# name.01234.5678.9012.meta or name.01234.5678.9012.log.tar.xz. We make sure +# "name" is sanitized in CrashCollector::Sanitize to not include any periods. get_base() { - echo "${1%.*}" + echo "$1" | cut -d. -f-4 } get_extension() { From 369af352e3911c7c8f77424045da2bc8494f22c8 Mon Sep 17 00:00:00 2001 From: Thiemo Nagel Date: Mon, 19 May 2014 14:08:51 +0200 Subject: [PATCH 168/225] crash_sender: remove obsolete is_on_3g(), improve comment BUG=None TEST=`cbuildbot x86-generic-full` passed Change-Id: I49e259d42b1edaf0fb43ee912fa99799ca87f08c Reviewed-on: https://chromium-review.googlesource.com/200440 Reviewed-by: Mike Frysinger Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_sender | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index e42cb023d..399258afd 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -188,11 +188,6 @@ generate_uniform_random() { echo $((random % max)) } -is_on_3g() { - # See crosbug.com/3304. - return 1 -} - # Check if sending a crash now does not exceed the maximum 24hr rate and # commit to doing so, if not. check_rate() { @@ -500,12 +495,11 @@ remove_report() { rm -f -- "${base}".* } -# Send all crashes from the given directory. +# Send all crashes from the given directory. This applies even when we're on a +# 3G connection (see crosbug.com/3304 for discussion). send_crashes() { local dir="$1" - # Cycle through minidumps, most recent first. That way if we're about - # to exceed the daily rate, we send the most recent minidumps. if [ ! -d "${dir}" ]; then return fi @@ -520,7 +514,9 @@ send_crashes() { fi done - # Look through all metadata (*.meta) files, if any exist. + # Look through all metadata (*.meta) files, oldest first. That way, the rate + # limit does not stall old crashes if there's a high amount of new crashes + # coming in. for meta_path in $(ls -1tr "${dir}"/*.meta 2>/dev/null); do lecho "Considering metadata ${meta_path}." local kind=$(get_kind "${meta_path}") @@ -550,11 +546,6 @@ send_crashes() { continue fi - if is_on_3g; then - lecho "Not sending crash reports while on 3G, saving for later." - return 0 - fi - if ! is_complete_metadata "${meta_path}"; then # This report is incomplete, so if it's old, just remove it. local old_meta=$(${FIND} "${dir}" -mindepth 1 -name \ From 3029e6facbf83701b1e777ddd513c45e9c2d523e Mon Sep 17 00:00:00 2001 From: Thiemo Nagel Date: Mon, 19 May 2014 14:18:41 +0200 Subject: [PATCH 169/225] crash_sender: add 30sec holdoff for fresh crashes As a rule, the .meta file of a crash report should only be created after the corresponding crash data files have been written to avoid the crash report to be sent prematurely without data files. Merely as an additional safeguard, this changeset prevents a crash report to be sent within 30 seconds of writing the .meta file. This functionality integrates with the existing sleep due to SECONDS_SEND_SPREAD. BUG=chromium:338977 TEST=`cbuildbot x86-generic-full` passes Change-Id: Id3651eb697527ca5b976274933b0ca2ee09870e2 Reviewed-on: https://chromium-review.googlesource.com/200061 Tested-by: Mike Frysinger Reviewed-by: Mike Frysinger Commit-Queue: Mike Frysinger --- crash_reporter/crash_sender | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 399258afd..9b96205be 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -288,7 +288,6 @@ send_crash() { local report_payload="$(get_key_value "${meta_path}" "payload")" local kind="$(get_kind "${meta_path}")" local exec_name="$(get_key_value "${meta_path}" "exec_name")" - local sleep_time=$(generate_uniform_random $SECONDS_SEND_SPREAD) local url="${REPORT_UPLOAD_PROD_URL}" local chromeos_version="$(get_key_value "${meta_path}" "ver")" local board="$(get_board)" @@ -386,7 +385,6 @@ send_crash() { if [ "${product}" != "${CHROMEOS_PRODUCT}" ]; then lecho " Sending crash report on behalf of ${product}" fi - lecho " Scheduled to send in ${sleep_time}s" lecho " Metadata: ${meta_path} (${kind})" lecho " Payload: ${report_payload}" lecho " Version: ${version}" @@ -417,11 +415,6 @@ send_crash() { return 0 fi - if ! sleep ${sleep_time}; then - lecho "Sleep failed" - return 1 - fi - # Read in the first proxy, if any, for a given URL. NOTE: The # double-quotes are necessary due to a bug in dash with the "local" # builtin command and values that have spaces in them (see @@ -564,6 +557,27 @@ send_crashes() { return 0 fi + # The .meta file should be written *after* all to-be-uploaded files that it + # references. Nevertheless, as a safeguard, a hold-off time of thirty + # seconds after writing the .meta file is ensured. Also, sending of crash + # reports is spread out randomly by up to SECONDS_SEND_SPREAD. Thus, for + # the sleep call the greater of the two delays is used. + local now=$(date +%s) + local holdoff_time=$(($(stat --format=%Y "${meta_path}") + 30 - ${now})) + local spread_time=$(generate_uniform_random "${SECONDS_SEND_SPREAD}") + local sleep_time + if [ ${spread_time} -gt ${holdoff_time} ]; then + sleep_time="${spread_time}" + else + sleep_time="${holdoff_time}" + fi + lecho "Scheduled to send in ${sleep_time}s." + if ! is_mock; then + if ! sleep "${sleep_time}"; then + lecho "Sleep failed" + return 1 + fi + fi if ! send_crash "${meta_path}"; then lecho "Problem sending ${meta_path}, not removing." continue @@ -609,7 +623,7 @@ parseargs() { usage 0 ;; *) - lecho "Uknown options: $*" + lecho "Unknown options: $*" exit 1 ;; esac From a39667d9082db2dbf02cb3ea656e765885fc5e82 Mon Sep 17 00:00:00 2001 From: Thiemo Nagel Date: Fri, 9 May 2014 16:05:13 +0200 Subject: [PATCH 170/225] crash_sender: ignore missing files to prevent upload failure Don't try to upload missing files, instead send the reports without them to avoid failure in curl, breaking the cycle of try-fail-try-fail. BUG=chromium:338977 TEST=manual test by copying the script to a Chromebook CQ-DEPEND=CL:200061 Change-Id: If35828b48b7746bdfae519e4777cc0d7e9245e0e Reviewed-on: https://chromium-review.googlesource.com/199070 Reviewed-by: Mike Frysinger Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_sender | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 9b96205be..43fd24c0c 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -308,7 +308,11 @@ send_crash() { -F "sig=${sig}" \ -F "sig2=${sig}" fi - if [ "${log}" != "undefined" ]; then + if [ -r "${report_payload}" ]; then + set -- "$@" \ + -F "upload_file_${kind}=@${report_payload}" + fi + if [ "${log}" != "undefined" -a -r "${log}" ]; then set -- "$@" \ -F "log=@${log}" fi @@ -325,8 +329,14 @@ send_crash() { # Product & version are handled separately. upload_var_prod) ;; upload_var_ver) ;; - upload_var_*) set -- "$@" -F "${upload_prefix}${k#upload_var_}=${v}" ;; - upload_file_*) set -- "$@" -F "${upload_prefix}${k#upload_file_}=@${v}" ;; + upload_var_*) + set -- "$@" -F "${upload_prefix}${k#upload_var_}=${v}" + ;; + upload_file_*) + if [ -r "${v}" ]; then + set -- "$@" -F "${upload_prefix}${k#upload_file_}=@${v}" + fi + ;; esac done @@ -430,7 +440,6 @@ send_crash() { --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \ -F "prod=${product}" \ -F "ver=${version}" \ - -F "upload_file_${kind}=@${report_payload}" \ -F "board=${board}" \ -F "hwclass=${hwclass}" \ -F "exec_name=${exec_name}" \ From f30c641225e55055641ffe3fd679a96e44f34af6 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Thu, 22 May 2014 23:09:01 -0700 Subject: [PATCH 171/225] crash-reporter: Update to build against libchrome-271506. BUG=chromium:375032 TEST=`FEATURES=test emerge-$BOARD platform2` Change-Id: I3984dee513690711e277e5fa8939ca5f7910d180 Reviewed-on: https://chromium-review.googlesource.com/201184 Tested-by: Ben Chan Commit-Queue: Ben Chan Reviewed-by: Mike Frysinger --- crash_reporter/chrome_collector.cc | 6 +-- crash_reporter/chrome_collector_test.cc | 6 +-- crash_reporter/crash_collector.cc | 4 +- crash_reporter/crash_collector_test.cc | 39 +++++++------------ crash_reporter/crash_reporter.cc | 2 +- crash_reporter/kernel_collector.cc | 2 +- crash_reporter/kernel_collector_test.cc | 3 +- crash_reporter/kernel_warning_collector.cc | 2 +- crash_reporter/list_proxies.cc | 4 +- crash_reporter/udev_collector_test.cc | 6 +-- crash_reporter/unclean_shutdown_collector.cc | 2 +- .../unclean_shutdown_collector_test.cc | 7 ++-- crash_reporter/user_collector.cc | 13 +++---- crash_reporter/user_collector_test.cc | 12 +++--- 14 files changed, 46 insertions(+), 62 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 9798ec538..7bf1f85df 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -72,9 +72,9 @@ bool GetDriErrorState(const chromeos::dbus::Proxy &proxy, guchar *decoded_error_state = g_base64_decode(error_state_str.c_str() + kBase64HeaderLength, &len); - int written; - written = file_util::WriteFile(error_state_path, - reinterpret_cast(decoded_error_state), len); + int written = + base::WriteFile(error_state_path, + reinterpret_cast(decoded_error_state), len); g_free(decoded_error_state); if (written < 0 || (gsize)written != len) { diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc index 37a180bfa..9b97399f7 100644 --- a/crash_reporter/chrome_collector_test.cc +++ b/crash_reporter/chrome_collector_test.cc @@ -123,13 +123,13 @@ TEST_F(ChromeCollectorTest, HandleCrash) { const FilePath& dir = scoped_temp_dir.path(); FilePath dump_file = dir.Append("test.dmp"); ASSERT_EQ(strlen(kCrashFormatWithFile), - file_util::WriteFile(dump_file, kCrashFormatWithFile, - strlen(kCrashFormatWithFile))); + base::WriteFile(dump_file, kCrashFormatWithFile, + strlen(kCrashFormatWithFile))); collector_.ForceCrashDirectory(dir); FilePath log_file; { - file_util::ScopedFILE output( + base::ScopedFILE output( base::CreateAndOpenTemporaryFileInDir(dir, &log_file)); ASSERT_TRUE(output.get()); base::AutoReset auto_reset_file_ptr(&collector_.output_file_ptr_, diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 5ddff085c..8e463da2c 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -102,7 +102,7 @@ int CrashCollector::WriteNewFile(const FilePath &filename, return -1; } - int rv = file_util::WriteFileDescriptor(fd, data, size); + int rv = base::WriteFileDescriptor(fd, data, size); IGNORE_EINTR(close(fd)); return rv; } @@ -520,7 +520,7 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, version.c_str(), payload_path.c_str(), payload_size); - // We must use WriteNewFile instead of file_util::WriteFile as we + // We must use WriteNewFile instead of base::WriteFile as we // do not want to write with root access to a symlink that an attacker // might have created. if (WriteNewFile(meta_path, meta_data.c_str(), meta_data.size()) < 0) { diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 863fed3f4..81a4a7c9b 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -175,22 +175,19 @@ bool CrashCollectorTest::CheckHasCapacity() { TEST_F(CrashCollectorTest, CheckHasCapacityUsual) { // Test kMaxCrashDirectorySize - 1 non-meta files can be added. for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { - file_util::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)), - "", 0); + base::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)), "", 0); EXPECT_TRUE(CheckHasCapacity()); } // Test an additional kMaxCrashDirectorySize - 1 meta files fit. for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { - file_util::WriteFile(test_dir_.Append(StringPrintf("file%d.meta", i)), - "", 0); + base::WriteFile(test_dir_.Append(StringPrintf("file%d.meta", i)), "", 0); EXPECT_TRUE(CheckHasCapacity()); } // Test an additional kMaxCrashDirectorySize meta files don't fit. for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) { - file_util::WriteFile(test_dir_.Append(StringPrintf("overage%d.meta", i)), - "", 0); + base::WriteFile(test_dir_.Append(StringPrintf("overage%d.meta", i)), "", 0); EXPECT_FALSE(CheckHasCapacity()); } } @@ -198,26 +195,25 @@ TEST_F(CrashCollectorTest, CheckHasCapacityUsual) { TEST_F(CrashCollectorTest, CheckHasCapacityCorrectBasename) { // Test kMaxCrashDirectorySize - 1 files can be added. for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) { - file_util::WriteFile(test_dir_.Append(StringPrintf("file.%d.core", i)), - "", 0); + base::WriteFile(test_dir_.Append(StringPrintf("file.%d.core", i)), "", 0); EXPECT_TRUE(CheckHasCapacity()); } - file_util::WriteFile(test_dir_.Append("file.last.core"), "", 0); + base::WriteFile(test_dir_.Append("file.last.core"), "", 0); EXPECT_FALSE(CheckHasCapacity()); } TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) { // Test many files with different extensions and same base fit. for (int i = 0; i < 5 * CrashCollector::kMaxCrashDirectorySize; ++i) { - file_util::WriteFile(test_dir_.Append(StringPrintf("a.%d", i)), "", 0); + base::WriteFile(test_dir_.Append(StringPrintf("a.%d", i)), "", 0); EXPECT_TRUE(CheckHasCapacity()); } // Test dot files are treated as individual files. for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 2; ++i) { - file_util::WriteFile(test_dir_.Append(StringPrintf(".file%d", i)), "", 0); + base::WriteFile(test_dir_.Append(StringPrintf(".file%d", i)), "", 0); EXPECT_TRUE(CheckHasCapacity()); } - file_util::WriteFile(test_dir_.Append("normal.meta"), "", 0); + base::WriteFile(test_dir_.Append("normal.meta"), "", 0); EXPECT_FALSE(CheckHasCapacity()); } @@ -238,7 +234,7 @@ TEST_F(CrashCollectorTest, ReadKeyValueFile) { std::map dictionary; std::map::iterator i; - file_util::WriteFile(path, contents, strlen(contents)); + base::WriteFile(path, contents, strlen(contents)); EXPECT_TRUE(collector_.ReadKeyValueFile(path, '=', &dictionary)); i = dictionary.find("a"); @@ -255,7 +251,7 @@ TEST_F(CrashCollectorTest, ReadKeyValueFile) { "=k\n" "#comment=0\n" "l=\n"); - file_util::WriteFile(path, contents, strlen(contents)); + base::WriteFile(path, contents, strlen(contents)); EXPECT_FALSE(collector_.ReadKeyValueFile(path, '=', &dictionary)); EXPECT_EQ(5, dictionary.size()); @@ -282,13 +278,9 @@ TEST_F(CrashCollectorTest, MetaData) { std::string contents; collector_.lsb_release_ = lsb_release.value(); const char kLsbContents[] = "CHROMEOS_RELEASE_VERSION=version\n"; - ASSERT_TRUE( - file_util::WriteFile(lsb_release, - kLsbContents, strlen(kLsbContents))); + ASSERT_TRUE(base::WriteFile(lsb_release, kLsbContents, strlen(kLsbContents))); const char kPayload[] = "foo"; - ASSERT_TRUE( - file_util::WriteFile(payload_file, - kPayload, strlen(kPayload))); + ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload))); collector_.AddCrashMetaData("foo", "bar"); collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value()); EXPECT_TRUE(base::ReadFileToString(meta_file, &contents)); @@ -303,9 +295,7 @@ TEST_F(CrashCollectorTest, MetaData) { // Test target of symlink is not overwritten. payload_file = test_dir_.Append("payload2-file"); - ASSERT_TRUE( - file_util::WriteFile(payload_file, - kPayload, strlen(kPayload))); + ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload))); FilePath meta_symlink_path = test_dir_.Append("symlink.meta"); ASSERT_EQ(0, symlink(kMetaFileBasename, @@ -337,8 +327,7 @@ TEST_F(CrashCollectorTest, GetLogContents) { const char kConfigContents[] = "foobar:echo hello there | sed -e \"s/there/world/\""; ASSERT_TRUE( - file_util::WriteFile(config_file, - kConfigContents, strlen(kConfigContents))); + base::WriteFile(config_file, kConfigContents, strlen(kConfigContents))); base::DeleteFile(FilePath(output_file), false); EXPECT_FALSE(collector_.GetLogContents(config_file, "barfoo", diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 828ec9194..be70ebcde 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -68,7 +68,7 @@ static bool IsFeedbackAllowed() { } static bool TouchFile(const FilePath &file_path) { - return file_util::WriteFile(file_path, "", 0) == 0; + return base::WriteFile(file_path, "", 0) == 0; } static void SendCrashMetrics(CrashKinds type, const char* name) { diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 7f7c7fb95..a57e58749 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -523,7 +523,7 @@ bool KernelCollector::Collect() { FilePath kernel_crash_path = root_crash_directory.Append( StringPrintf("%s.kcrash", dump_basename.c_str())); - // We must use WriteNewFile instead of file_util::WriteFile as we + // We must use WriteNewFile instead of base::WriteFile as we // do not want to write with root access to a symlink that an attacker // might have created. if (WriteNewFile(kernel_crash_path, diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 14b8357fd..5e9bc15b1 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -33,8 +33,7 @@ class KernelCollectorTest : public ::testing::Test { protected: void WriteStringToFile(const FilePath &file_path, const char *data) { - ASSERT_EQ(strlen(data), - file_util::WriteFile(file_path, data, strlen(data))); + ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data))); } void SetUpSuccessfulCollect(); diff --git a/crash_reporter/kernel_warning_collector.cc b/crash_reporter/kernel_warning_collector.cc index 34d29a58f..a186e9f3a 100644 --- a/crash_reporter/kernel_warning_collector.cc +++ b/crash_reporter/kernel_warning_collector.cc @@ -77,7 +77,7 @@ bool KernelWarningCollector::Collect() { FilePath kernel_crash_path = root_crash_directory.Append( StringPrintf("%s.kcrash", dump_basename.c_str())); - // We must use WriteNewFile instead of file_util::WriteFile as we + // We must use WriteNewFile instead of base::WriteFile as we // do not want to write with root access to a symlink that an attacker // might have created. if (WriteNewFile(kernel_crash_path, diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc index 1f9db763c..41dd39046 100644 --- a/crash_reporter/list_proxies.cc +++ b/crash_reporter/list_proxies.cc @@ -66,7 +66,7 @@ std::deque ParseProxyString(const std::string &input) { base::StringTokenizer entry_tok(input, ";"); while (entry_tok.GetNext()) { std::string token = entry_tok.token(); - TrimWhitespaceASCII(token, TRIM_ALL, &token); + base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token); // Start by finding the first space (if any). std::string::iterator space; @@ -90,7 +90,7 @@ std::deque ParseProxyString(const std::string &input) { continue; // Invalid proxy scheme std::string host_and_port = std::string(space, token.end()); - TrimWhitespaceASCII(host_and_port, TRIM_ALL, &host_and_port); + base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port); if (scheme != "direct" && host_and_port.empty()) continue; // Must supply host/port when non-direct proxy used. ret.push_back(scheme + "://" + host_and_port); diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc index db1550fd6..d5d6f29bd 100644 --- a/crash_reporter/udev_collector_test.cc +++ b/crash_reporter/udev_collector_test.cc @@ -68,9 +68,9 @@ class UdevCollectorTest : public ::testing::Test { // Write to a dummy log config file. ASSERT_EQ(strlen(kLogConfigFileContents), - file_util::WriteFile(log_config_path, - kLogConfigFileContents, - strlen(kLogConfigFileContents))); + base::WriteFile(log_config_path, + kLogConfigFileContents, + strlen(kLogConfigFileContents))); chromeos::ClearLog(); } diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc index ae0775cb5..8ee83c88b 100644 --- a/crash_reporter/unclean_shutdown_collector.cc +++ b/crash_reporter/unclean_shutdown_collector.cc @@ -29,7 +29,7 @@ UncleanShutdownCollector::~UncleanShutdownCollector() { bool UncleanShutdownCollector::Enable() { FilePath file_path(unclean_shutdown_file_); base::CreateDirectory(file_path.DirName()); - if (file_util::WriteFile(file_path, "", 0) != 0) { + if (base::WriteFile(file_path, "", 0) != 0) { LOG(ERROR) << "Unable to create shutdown check file"; return false; } diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc index f9ad05466..a0eec1cc2 100644 --- a/crash_reporter/unclean_shutdown_collector_test.cc +++ b/crash_reporter/unclean_shutdown_collector_test.cc @@ -45,8 +45,7 @@ class UncleanShutdownCollectorTest : public ::testing::Test { protected: void WriteStringToFile(const FilePath &file_path, const char *data) { - ASSERT_EQ(strlen(data), - file_util::WriteFile(file_path, data, strlen(data))); + ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data))); } UncleanShutdownCollector collector_; @@ -87,7 +86,7 @@ TEST_F(UncleanShutdownCollectorTest, CollectFalse) { TEST_F(UncleanShutdownCollectorTest, CollectDeadBatterySuspended) { ASSERT_TRUE(collector_.Enable()); ASSERT_TRUE(base::PathExists(test_unclean_)); - file_util::WriteFile(collector_.powerd_suspended_file_, "", 0); + base::WriteFile(collector_.powerd_suspended_file_, "", 0); ASSERT_FALSE(collector_.Collect()); ASSERT_FALSE(base::PathExists(test_unclean_)); ASSERT_FALSE(base::PathExists(collector_.powerd_suspended_file_)); @@ -114,7 +113,7 @@ TEST_F(UncleanShutdownCollectorTest, CantDisable) { << "Error while creating directory '" << kTestUnclean << "': " << strerror(errno); } - ASSERT_EQ(0, file_util::WriteFile(test_unclean_.Append("foo"), "", 0)) + ASSERT_EQ(0, base::WriteFile(test_unclean_.Append("foo"), "", 0)) << "Error while creating empty file '" << test_unclean_.Append("foo").value() << "': " << strerror(errno); ASSERT_FALSE(collector_.Disable()); diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 5b0bdb357..3355b0e04 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -117,18 +117,15 @@ bool UserCollector::SetUpInternal(bool enabled) { CHECK(initialized_); LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling"; - if (file_util::WriteFile(FilePath(core_pipe_limit_file_), - kCorePipeLimit, - strlen(kCorePipeLimit)) != + if (base::WriteFile(FilePath(core_pipe_limit_file_), kCorePipeLimit, + strlen(kCorePipeLimit)) != static_cast(strlen(kCorePipeLimit))) { PLOG(ERROR) << "Unable to write " << core_pipe_limit_file_; return false; } std::string pattern = GetPattern(enabled); - if (file_util::WriteFile(FilePath(core_pattern_file_), - pattern.c_str(), - pattern.length()) != - static_cast(pattern.length())) { + if (base::WriteFile(FilePath(core_pattern_file_), pattern.c_str(), + pattern.length()) != static_cast(pattern.length())) { PLOG(ERROR) << "Unable to write " << core_pattern_file_; return false; } @@ -209,7 +206,7 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid, } FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); - // We must use WriteNewFile instead of file_util::WriteFile as we do + // We must use WriteNewFile instead of base::WriteFile as we do // not want to write with root access to a symlink that an attacker // might have created. if (WriteNewFile(log_path, error_log.data(), error_log.length()) < 0) { diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 0dd35e0d4..eeaf070b7 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -478,13 +478,13 @@ TEST_F(UserCollectorTest, ValidateProcFiles) { // maps file is empty FilePath maps_file = container_dir.Append("maps"); - ASSERT_EQ(0, file_util::WriteFile(maps_file, NULL, 0)); + ASSERT_EQ(0, base::WriteFile(maps_file, NULL, 0)); ASSERT_TRUE(base::PathExists(maps_file)); EXPECT_FALSE(collector_.ValidateProcFiles(container_dir)); // maps file is not empty const char data[] = "test data"; - ASSERT_EQ(sizeof(data), file_util::WriteFile(maps_file, data, sizeof(data))); + ASSERT_EQ(sizeof(data), base::WriteFile(maps_file, data, sizeof(data))); ASSERT_TRUE(base::PathExists(maps_file)); EXPECT_TRUE(collector_.ValidateProcFiles(container_dir)); } @@ -512,26 +512,26 @@ TEST_F(UserCollectorTest, ValidateCoreFile) { #endif // Core file has the expected header - ASSERT_TRUE(file_util::WriteFile(core_file, e_ident, sizeof(e_ident))); + ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident))); EXPECT_EQ(UserCollector::kErrorNone, collector_.ValidateCoreFile(core_file)); #if __WORDSIZE == 64 // 32-bit core file on 64-bit platform e_ident[EI_CLASS] = ELFCLASS32; - ASSERT_TRUE(file_util::WriteFile(core_file, e_ident, sizeof(e_ident))); + ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident))); EXPECT_EQ(UserCollector::kErrorUnsupported32BitCoreFile, collector_.ValidateCoreFile(core_file)); e_ident[EI_CLASS] = ELFCLASS64; #endif // Invalid core files - ASSERT_TRUE(file_util::WriteFile(core_file, e_ident, sizeof(e_ident) - 1)); + ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident) - 1)); EXPECT_EQ(UserCollector::kErrorInvalidCoreFile, collector_.ValidateCoreFile(core_file)); e_ident[EI_MAG0] = 0; - ASSERT_TRUE(file_util::WriteFile(core_file, e_ident, sizeof(e_ident))); + ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident))); EXPECT_EQ(UserCollector::kErrorInvalidCoreFile, collector_.ValidateCoreFile(core_file)); } From 0e31ecbde59cc882bd4c4d04ba2cda0570dcecdf Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 27 May 2014 16:33:54 -0400 Subject: [PATCH 172/225] crash-sender: set timeout to 4 hours A single run of crash_sender might take some time to upload a report (if the connection is pretty slow). Give the system 4 hours to upload at least one report. BUG=chromium:377981 TEST=`cbuildbot x86-generic-full` passes CQ-DEPEND=CL:201719 Change-Id: I4d3870b961653c7f0ca85e1015f371919125e846 Reviewed-on: https://chromium-review.googlesource.com/201404 Reviewed-by: Gaurav Shah Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/init/crash-sender.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/init/crash-sender.conf b/crash_reporter/init/crash-sender.conf index 25ad2f8c8..892186f91 100644 --- a/crash_reporter/init/crash-sender.conf +++ b/crash_reporter/init/crash-sender.conf @@ -8,4 +8,4 @@ author "chromium-os-dev@chromium.org" start on starting system-services stop on stopping system-services -exec periodic_scheduler 3600 crash_sender /sbin/crash_sender +exec periodic_scheduler 3600 14400 crash_sender /sbin/crash_sender From 47785c0e17c718d4446b7f6d8cdf3e177859e2dd Mon Sep 17 00:00:00 2001 From: Thiemo Nagel Date: Mon, 19 May 2014 14:31:05 +0200 Subject: [PATCH 173/225] crash_sender: re-order send_crashes() logic send_crashes() is re-ordered to make sure that crashes are deleted at the earliest possible point in time, especially that crash deleting cannot be blocked if the crash upload pipeline is blocked for some reason. The intention is to free disk space as soon as possible, but there are also privacy implications of removing crash data as soon as it is determined that the user has not consented to its upload. BUG=chromium:338977 TEST=`cbuildbot x86-generic-full` passes CQ-DEPEND=CL:200553 Change-Id: I797cf8430185b0d2a4c3e615c6803e95b59c8ac7 Reviewed-on: https://chromium-review.googlesource.com/200060 Tested-by: Mike Frysinger Reviewed-by: Ben Chan Commit-Queue: Mike Frysinger --- crash_reporter/crash_sender | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 43fd24c0c..2b27e3099 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -519,10 +519,13 @@ send_crashes() { # Look through all metadata (*.meta) files, oldest first. That way, the rate # limit does not stall old crashes if there's a high amount of new crashes # coming in. + # For each crash report, first evaluate conditions that might lead to its + # removal to honor user choice and to free disk space as soon as possible, + # then decide whether it should be sent right now or kept for later sending. for meta_path in $(ls -1tr "${dir}"/*.meta 2>/dev/null); do lecho "Considering metadata ${meta_path}." - local kind=$(get_kind "${meta_path}") + local kind=$(get_kind "${meta_path}") if [ "${kind}" != "minidump" ] && \ [ "${kind}" != "kcrash" ] && \ [ "${kind}" != "log" ]; then @@ -531,13 +534,10 @@ send_crashes() { continue fi - if ${METRICS_CLIENT} -g; then - lecho "Guest mode has been entered. Delaying crash sending until exited." - return 0 - fi - + # Remove existing crashes in case user consent has not (yet) been given or + # has been revoked. if ! ${METRICS_CLIENT} -c; then - lecho "Uploading is disabled. Removing crash." + lecho "Crash reporting is disabled. Removing crash." remove_report "${meta_path}" continue fi @@ -561,9 +561,20 @@ send_crashes() { continue fi + # Don't send crash reports from previous sessions while we're in guest mode + # to avoid the impression that crash reporting was enabled, which it isn't. + # (Don't exit right now because subsequent reports may be candidates for + # deletion.) + if ${METRICS_CLIENT} -g; then + lecho "Guest mode has been entered. Delaying crash sending until exited." + continue + fi + + # Skip report if the upload rate is exceeded. (Don't exit right now because + # subsequent reports may be candidates for deletion.) if ! check_rate; then lecho "Sending ${meta_path} would exceed rate. Leaving for later." - return 0 + continue fi # The .meta file should be written *after* all to-be-uploaded files that it @@ -587,6 +598,8 @@ send_crashes() { return 1 fi fi + + # Try to upload. if ! send_crash "${meta_path}"; then lecho "Problem sending ${meta_path}, not removing." continue From 14129966b08ac0c46742628981fc92e9b16ba160 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Sun, 15 Sep 2013 14:15:01 -0400 Subject: [PATCH 174/225] crash_sender: delete redundant logic for parallel runs A recent commit added global locking support via `flock` (see CL:65556). With that in place, we no longer need this ad-hoc pid checking. Advantage is that the flock is race free (both with file reads/writes and pid #s). BUG=chromium:199491 TEST=`cbuildbot x86-generic-full` works TEST=`crash_sender` on board works CQ-DEPEND=CL:201495 Change-Id: I00d48544856cc5fad60c6129f5c193abdde45f02 Reviewed-on: https://chromium-review.googlesource.com/169486 Reviewed-by: Rohit Makasana Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_sender | 43 ++++++++++--------------------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 2b27e3099..8c15ad7c1 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -9,11 +9,6 @@ set -e # Default product ID in crash report (used if GOOGLE_CRASH_* is undefined). CHROMEOS_PRODUCT=ChromeOS -# Should remove the run file when this process finishes. We don't want -# to always remove it since it may be for pre-existing crash_sender -# process. -CLEAN_UP_RUN_FILE=0 - # File whose existence implies crash reports may be sent, and whose # contents includes our machine's anonymized guid. CONSENT_ID="/home/chronos/Consent To Send Stats" @@ -107,9 +102,11 @@ cleanup() { if [ -n "${TMP_DIR}" ]; then rm -rf "${TMP_DIR}" fi - if [ ${CLEAN_UP_RUN_FILE} -eq 1 ]; then - rm -f "${RUN_FILE}" - fi + rm -f "${RUN_FILE}" + crash_done +} + +crash_done() { if is_mock; then # For testing purposes, emit a message to log so that we # know when the test has received all the messages from this run. @@ -117,28 +114,6 @@ cleanup() { fi } -check_not_already_running() { - set -o noclobber - if echo $$ 2>/dev/null > "${RUN_FILE}"; then - # Able to write RUN_FILE without contention. - CLEAN_UP_RUN_FILE=1 - set +o noclobber - return - fi - set +o noclobber - local last_pid=$(cat "${RUN_FILE}") - if [ ! -f "/proc/${last_pid}/cmdline" ]; then - CLEAN_UP_RUN_FILE=1 - # Note that this write may be executed by two crash_senders who - # simulataneously reap the existing dangling run file - echo $$ > "${RUN_FILE}" - return - fi - # This could just be an unrelated process, but it's ok to be conservative. - lecho "Already running. Exiting now." - exit 1 -} - is_official_image() { [ ${FORCE_OFFICIAL} -ne 0 ] && return 0 grep ^CHROMEOS_RELEASE_DESCRIPTION /etc/lsb-release | grep -q Official @@ -669,7 +644,10 @@ main() { exit 1 fi - check_not_already_running + # We don't perform checks on this because we have a master lock with the + # CRASH_SENDER_LOCK file. This pid file is for the system to keep track + # (like with autotests) that we're still running. + echo $$ > "${RUN_FILE}" for dependency in "${FIND}" "${METRICS_CLIENT}" \ "${RESTRICTED_CERTIFICATES_PATH}"; do @@ -693,7 +671,8 @@ main() { ( if ! flock -n 9; then - lecho "crash_sender is already running; quitting." + lecho "Already running; quitting." + crash_done exit 1 fi main "$@" From 6f970ed8fcda8ab2340d8b9961bc7b5686043000 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Wed, 28 May 2014 16:22:43 -0700 Subject: [PATCH 175/225] crash-reporter: Update gyp file to use src/platform2/common-mk We are moving the packages built by platform2's ebuild into src/platform2. We need packages to use src/platform2/common-mk instead of src/platform/common-mk. BUG=chromium:378554 TEST=emerge-daisy platform2 TEST=trybot run on daisy, link, mario and duck CQ-DEPEND=CL:201938 Change-Id: Ic154c59f61f7f95df5678a2033b74983b12f4dec Reviewed-on: https://chromium-review.googlesource.com/201967 Tested-by: Bertrand Simonnet Reviewed-by: Ben Chan Commit-Queue: Bertrand Simonnet --- crash_reporter/crash-reporter.gyp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index daa1ed0e7..fcb3d59df 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -97,7 +97,7 @@ 'sources': [ 'warn_collector.l', ], - 'includes': ['../common-mk/lex.gypi'], + 'includes': ['../../platform2/common-mk/lex.gypi'], }, ], 'conditions': [ @@ -106,7 +106,7 @@ { 'target_name': 'chrome_collector_test', 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], + 'includes': ['../../platform2/common-mk/common_test.gypi'], 'dependencies': ['libcrash'], 'sources': [ 'chrome_collector_test.cc', @@ -115,7 +115,7 @@ { 'target_name': 'crash_collector_test', 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], + 'includes': ['../../platform2/common-mk/common_test.gypi'], 'dependencies': ['libcrash'], 'sources': [ 'crash_collector_test.cc', @@ -124,7 +124,7 @@ { 'target_name': 'kernel_collector_test', 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], + 'includes': ['../../platform2/common-mk/common_test.gypi'], 'dependencies': ['libcrash'], 'sources': [ 'kernel_collector_test.cc', @@ -133,7 +133,7 @@ { 'target_name': 'udev_collector_test', 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], + 'includes': ['../../platform2/common-mk/common_test.gypi'], 'dependencies': ['libcrash'], 'sources': [ 'udev_collector_test.cc', @@ -142,7 +142,7 @@ { 'target_name': 'unclean_shutdown_collector_test', 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], + 'includes': ['../../platform2/common-mk/common_test.gypi'], 'dependencies': ['libcrash'], 'sources': [ 'unclean_shutdown_collector_test.cc', @@ -151,7 +151,7 @@ { 'target_name': 'user_collector_test', 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], + 'includes': ['../../platform2/common-mk/common_test.gypi'], 'dependencies': ['libcrash'], 'sources': [ 'user_collector_test.cc', From cb373dd610e32343630372ce0eafa447f82689ef Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Mon, 2 Jun 2014 14:12:02 -0700 Subject: [PATCH 176/225] crash-reporter: Update gyp files to use libchromeos from src/platform2 libchromeos was moved from src/platform to src/platform2. This CL updates the gyp files to use the new location. BUG=chromium:370258 TEST=FEATURES=test emerge-amd64-generic platform2 TEST=trybot run on daisy, link, mario and duck CQ-DEPEND=CL:202334 Change-Id: I8536a08aee17073be8544b7e60b205aafb2e0828 Reviewed-on: https://chromium-review.googlesource.com/202337 Reviewed-by: Prathmesh Prabhu Tested-by: Bertrand Simonnet Commit-Queue: Bertrand Simonnet --- crash_reporter/crash-reporter.gyp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index fcb3d59df..e1e728ea9 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -33,7 +33,7 @@ }, }, 'dependencies': [ - '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', + '../../platform2/libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', ], 'sources': [ 'chrome_collector.cc', @@ -56,7 +56,7 @@ }, 'dependencies': [ 'libcrash', - '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', + '../../platform2/libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', ], 'sources': [ @@ -74,7 +74,7 @@ ], }, 'dependencies': [ - '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', + '../../platform2/libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', ], 'sources': [ 'list_proxies.cc', From f7181dfe7a7381569783a170a56a02c763db8469 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Wed, 4 Jun 2014 13:14:58 -0700 Subject: [PATCH 177/225] crash-reporter: Migrate libchromeos to its own ebuild All platform2 packages should have their own ebuild that will be compiled independently. Packages should DEPEND on libchromeos and should not have a gyp dependency on libchromeos's gyp file anymore. BUG=chromium:381372 TEST=emerge-daisy libchromeos && emerge-daisy platform2 work TEST=trybot run on daisy, link, mario and duck. TEST=trybot run on lumpy-incremental-paladin. CQ-DEPEND=CL:202748 Change-Id: I2f6fe6e8572a533d74931db2dd850fe31d9041dc Reviewed-on: https://chromium-review.googlesource.com/202762 Reviewed-by: Mike Frysinger Tested-by: Bertrand Simonnet Commit-Queue: Bertrand Simonnet --- crash_reporter/crash-reporter.gyp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index e1e728ea9..dfa95ec8d 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -1,9 +1,12 @@ { # Shouldn't need this, but doesn't work otherwise. - # http://crbug.com/340086 + # http://crbug.com/340086 and http://crbug.com/385186 + # Note: the unused dependencies are optimized out by the compiler. 'target_defaults': { 'variables': { 'deps': [ + 'libchromeos-<(libbase_ver)', + 'dbus-glib-1', ], }, }, @@ -33,7 +36,7 @@ }, }, 'dependencies': [ - '../../platform2/libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', + '<(platform_root)/system_api/system_api.gyp:system_api-headers', ], 'sources': [ 'chrome_collector.cc', @@ -56,7 +59,6 @@ }, 'dependencies': [ 'libcrash', - '../../platform2/libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', ], 'sources': [ @@ -73,9 +75,6 @@ 'libchrome-<(libbase_ver)', ], }, - 'dependencies': [ - '../../platform2/libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', - ], 'sources': [ 'list_proxies.cc', ], From 7e77690375bc8a896a8de318d69d515e67c7aefe Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Wed, 18 Jun 2014 13:19:51 -0700 Subject: [PATCH 178/225] crash-reporter: Fix coding style and cpplint issues. BUG=chromium:385849 TEST=`FEATURES=test emerge-$BOARD platform2` TEST=`cpplint.py --filter=--build/header_guard,-build/include_order *.h *.cc` Change-Id: I3c24dd9487df50cdb22fc1c7739c9e95e452afae Reviewed-on: https://chromium-review.googlesource.com/204487 Tested-by: Ben Chan Reviewed-by: Mike Frysinger Commit-Queue: Ben Chan --- crash_reporter/chrome_collector.cc | 16 +++--- crash_reporter/chrome_collector.h | 11 ++-- crash_reporter/chrome_collector_test.cc | 15 +++--- crash_reporter/crash_collector.cc | 23 +++++---- crash_reporter/crash_collector.h | 10 ++-- crash_reporter/crash_collector_test.cc | 21 ++++---- crash_reporter/crash_collector_test.h | 8 +-- crash_reporter/crash_reporter.cc | 14 ++--- crash_reporter/kernel_collector.cc | 43 ++++++++-------- crash_reporter/kernel_collector.h | 11 ++-- crash_reporter/kernel_collector_test.cc | 18 +++---- crash_reporter/kernel_warning_collector.cc | 12 ++--- crash_reporter/kernel_warning_collector.h | 9 ++-- crash_reporter/list_proxies.cc | 21 ++++---- crash_reporter/udev_collector.cc | 16 +++--- crash_reporter/udev_collector.h | 13 ++--- crash_reporter/udev_collector_test.cc | 11 ++-- crash_reporter/unclean_shutdown_collector.cc | 4 +- crash_reporter/unclean_shutdown_collector.h | 11 ++-- .../unclean_shutdown_collector_test.cc | 13 ++--- crash_reporter/user_collector.cc | 51 ++++++++++--------- crash_reporter/user_collector.h | 11 ++-- crash_reporter/user_collector_test.cc | 15 +++--- crash_reporter/warn_collector_test.c | 3 +- 24 files changed, 198 insertions(+), 182 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 7bf1f85df..91a926bf8 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -6,6 +6,7 @@ #include #include + #include #include @@ -13,10 +14,10 @@ #include #include #include -#include "chromeos/process.h" -#include "chromeos/syslog_logging.h" -#include "chromeos/dbus/dbus.h" -#include "chromeos/dbus/service_constants.h" +#include +#include +#include +#include const char kDefaultMinidumpName[] = "upload_file_minidump"; const char kTarPath[] = "/bin/tar"; @@ -125,7 +126,8 @@ bool GetAdditionalLogs(const FilePath &log_path) { return true; } -} //namespace + +} // namespace ChromeCollector::ChromeCollector() : output_file_ptr_(stdout) {} @@ -205,13 +207,13 @@ bool ChromeCollector::ParseCrashLog(const std::string &data, LOG(ERROR) << "Can't find : after name @ offset " << at; break; } - at += name.size() + 1; // Skip the name & : delimiter. + at += name.size() + 1; // Skip the name & : delimiter. if (!GetDelimitedString(data, ':', at, &size_string)) { LOG(ERROR) << "Can't find : after size @ offset " << at; break; } - at += size_string.size() + 1; // Skip the size & : delimiter. + at += size_string.size() + 1; // Skip the size & : delimiter. size_t size; if (!base::StringToSizeT(size_string, &size)) { diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h index c6dbfc944..3586d52ec 100644 --- a/crash_reporter/chrome_collector.h +++ b/crash_reporter/chrome_collector.h @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef _CRASH_REPORTER_CHROME_COLLECTOR_H_ -#define _CRASH_REPORTER_CHROME_COLLECTOR_H_ +#ifndef CRASH_REPORTER_CHROME_COLLECTOR_H_ +#define CRASH_REPORTER_CHROME_COLLECTOR_H_ #include -#include "base/files/file_path.h" +#include +#include // for FRIEND_TEST + #include "crash-reporter/crash_collector.h" -#include "gtest/gtest_prod.h" // for FRIEND_TEST class SystemLogging; @@ -49,4 +50,4 @@ class ChromeCollector : public CrashCollector { FILE *output_file_ptr_; }; -#endif +#endif // CRASH_REPORTER_CHROME_COLLECTOR_H_ diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc index 9b97399f7..843ca8e67 100644 --- a/crash_reporter/chrome_collector_test.cc +++ b/crash_reporter/chrome_collector_test.cc @@ -2,17 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "crash-reporter/chrome_collector.h" + #include #include -#include "base/auto_reset.h" -#include "base/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "chromeos/syslog_logging.h" -#include "chromeos/test_helpers.h" -#include "crash-reporter/chrome_collector.h" -#include "gtest/gtest.h" +#include +#include +#include +#include +#include +#include using base::FilePath; diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 8e463da2c..152aec323 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -10,25 +10,26 @@ #include // for mode_t. #include // For waitpid. #include // For execv and fork. -#define __STDC_FORMAT_MACROS // PRId64 +#define __STDC_FORMAT_MACROS // PRId64 #include #include +#include #include #include #include -#include "base/file_util.h" -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "chromeos/cryptohome.h" -#include "chromeos/dbus/dbus.h" -#include "chromeos/dbus/service_constants.h" -#include "chromeos/process.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include static const char kCollectChromeFile[] = "/mnt/stateful_partition/etc/collect_chrome_crashes"; diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 67ff87089..0e786618d 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef _CRASH_REPORTER_CRASH_COLLECTOR_H_ -#define _CRASH_REPORTER_CRASH_COLLECTOR_H_ +#ifndef CRASH_REPORTER_CRASH_COLLECTOR_H_ +#define CRASH_REPORTER_CRASH_COLLECTOR_H_ #include @@ -12,8 +12,8 @@ #include -#include "base/files/file_path.h" -#include "gtest/gtest_prod.h" // for FRIEND_TEST +#include +#include // for FRIEND_TEST // User crash collector. class CrashCollector { @@ -172,4 +172,4 @@ class CrashCollector { base::FilePath log_config_path_; }; -#endif // _CRASH_REPORTER_CRASH_COLLECTOR_H_ +#endif // CRASH_REPORTER_CRASH_COLLECTOR_H_ diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 81a4a7c9b..1ffb8e423 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -9,13 +9,14 @@ #include #include -#include "base/file_util.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "chromeos/syslog_logging.h" -#include "chromeos/test_helpers.h" +#include +#include +#include +#include +#include +#include + #include "crash-reporter/crash_collector.h" -#include "gtest/gtest.h" using base::FilePath; using base::StringPrintf; @@ -115,12 +116,12 @@ TEST_F(CrashCollectorTest, GetCrashDirectoryInfo) { EXPECT_EQ(kRootGid, directory_group); // No need to destroy the hash as GetCrashDirectoryInfo() will do it for us. - GHashTable *active_sessions = g_hash_table_new (g_str_hash, g_str_equal); + GHashTable *active_sessions = g_hash_table_new(g_str_hash, g_str_equal); char kUser[] = "chicken@butt.com"; char kHash[] = "hashcakes"; - g_hash_table_insert (active_sessions, - static_cast(kUser), - static_cast(kHash)); + g_hash_table_insert(active_sessions, + static_cast(kUser), + static_cast(kHash)); EXPECT_CALL(collector_, GetActiveUserSessions()) .WillOnce(Return(active_sessions)); diff --git a/crash_reporter/crash_collector_test.h b/crash_reporter/crash_collector_test.h index 71b42b7dd..28811b04e 100644 --- a/crash_reporter/crash_collector_test.h +++ b/crash_reporter/crash_collector_test.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef _CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_ -#define _CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_ +#ifndef CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_ +#define CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_ #include "crash-reporter/crash_collector.h" @@ -14,7 +14,7 @@ class CrashCollectorMock : public CrashCollector { public: - MOCK_METHOD0(GetActiveUserSessions, GHashTable *()); + MOCK_METHOD0(GetActiveUserSessions, GHashTable*()); }; -#endif // _CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_ +#endif // CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_ diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index be70ebcde..8aafc4677 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -3,27 +3,27 @@ // found in the LICENSE file. #include // for open +#include #include #include -#include - -#include #include +#include #include #include #include #include -#include "chromeos/syslog_logging.h" +#include +#include +#include + #include "crash-reporter/chrome_collector.h" #include "crash-reporter/kernel_collector.h" #include "crash-reporter/kernel_warning_collector.h" #include "crash-reporter/udev_collector.h" #include "crash-reporter/unclean_shutdown_collector.h" #include "crash-reporter/user_collector.h" -#include "gflags/gflags.h" -#include "metrics/metrics_library.h" #pragma GCC diagnostic ignored "-Wstrict-aliasing" DEFINE_bool(init, false, "Initialize crash logging"); @@ -252,7 +252,7 @@ static void OpenStandardFileDescriptors() { // invalid fd. do { new_fd = open("/dev/null", 0); - CHECK(new_fd >= 0) << "Unable to open /dev/null"; + CHECK_GE(new_fd, 0) << "Unable to open /dev/null"; } while (new_fd >= 0 && new_fd <= 2); close(new_fd); } diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index a57e58749..2e93f2b98 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -4,10 +4,12 @@ #include "crash-reporter/kernel_collector.h" -#include "base/file_util.h" -#include "base/logging.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" +#include + +#include +#include +#include +#include static const char kDefaultKernelStackSignature[] = "kernel-UnspecifiedStackSignature"; @@ -27,17 +29,17 @@ static const int kSignatureTimestampWindow = 2; // Kernel log timestamp regular expression. static const std::string kTimestampRegex("^<.*>\\[\\s*(\\d+\\.\\d+)\\]"); -/* - * These regular expressions enable to us capture the PC in a backtrace. - * The backtrace is obtained through dmesg or the kernel's preserved/kcrashmem - * feature. - * - * For ARM we see: - * "<5>[ 39.458982] PC is at write_breakme+0xd0/0x1b4" - * For x86: - * "<0>[ 37.474699] EIP: [<790ed488>] write_breakme+0x80/0x108 \ - * SS:ESP 0068:e9dd3efc - */ +// +// These regular expressions enable to us capture the PC in a backtrace. +// The backtrace is obtained through dmesg or the kernel's preserved/kcrashmem +// feature. +// +// For ARM we see: +// "<5>[ 39.458982] PC is at write_breakme+0xd0/0x1b4" +// For x86: +// "<0>[ 37.474699] EIP: [<790ed488>] write_breakme+0x80/0x108 +// SS:ESP 0068:e9dd3efc" +// static const char *s_pc_regex[] = { 0, " PC is at ([^\\+ ]+).*", @@ -85,7 +87,7 @@ bool KernelCollector::ReadRecordToString(std::string *contents, return false; } - if (record_re.FullMatch(record, &captured)){ + if (record_re.FullMatch(record, &captured)) { // Found a match, append it to the content, and remove from pstore. contents->append(captured); base::DeleteFile(ramoops_record, false); @@ -231,8 +233,7 @@ bool KernelCollector::Enable() { s_pc_regex[arch_] == NULL) { LOG(WARNING) << "KernelCollector does not understand this architecture"; return false; - } - else { + } else { FilePath ramoops_record; GetRamoopsRecordPath(&ramoops_record, 0); if (!base::PathExists(ramoops_record)) { @@ -349,8 +350,7 @@ void KernelCollector::ProcessStackTrace( } } -enum KernelCollector::ArchKind KernelCollector::GetCompilerArch(void) -{ +enum KernelCollector::ArchKind KernelCollector::GetCompilerArch(void) { #if defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY) return archArm; #elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_64) @@ -362,8 +362,7 @@ enum KernelCollector::ArchKind KernelCollector::GetCompilerArch(void) #endif } -void KernelCollector::SetArch(enum ArchKind arch) -{ +void KernelCollector::SetArch(enum ArchKind arch) { arch_ = arch; } diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index c965cd39d..8f52e440e 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -2,16 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef _CRASH_REPORTER_KERNEL_COLLECTOR_H_ -#define _CRASH_REPORTER_KERNEL_COLLECTOR_H_ +#ifndef CRASH_REPORTER_KERNEL_COLLECTOR_H_ +#define CRASH_REPORTER_KERNEL_COLLECTOR_H_ #include #include -#include "base/files/file_path.h" +#include +#include // for FRIEND_TEST + #include "crash-reporter/crash_collector.h" -#include "gtest/gtest_prod.h" // for FRIEND_TEST // Kernel crash collector. class KernelCollector : public CrashCollector { @@ -103,4 +104,4 @@ class KernelCollector : public CrashCollector { enum ArchKind arch_; }; -#endif // _CRASH_REPORTER_KERNEL_COLLECTOR_H_ +#endif // CRASH_REPORTER_KERNEL_COLLECTOR_H_ diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 5e9bc15b1..2644891ef 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -2,16 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "crash-reporter/kernel_collector.h" + #include -#include "base/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "chromeos/syslog_logging.h" -#include "chromeos/test_helpers.h" -#include "crash-reporter/kernel_collector.h" -#include "gtest/gtest.h" +#include +#include +#include +#include +#include +#include +#include static int s_crashes = 0; static bool s_metrics = false; @@ -280,7 +281,6 @@ TEST_F(KernelCollectorTest, CollectOK) { std::string contents; ASSERT_TRUE(base::ReadFileToString(FilePath(filename), &contents)); ASSERT_EQ("something", contents); - } // Perform tests which are common across architectures diff --git a/crash_reporter/kernel_warning_collector.cc b/crash_reporter/kernel_warning_collector.cc index a186e9f3a..7082569de 100644 --- a/crash_reporter/kernel_warning_collector.cc +++ b/crash_reporter/kernel_warning_collector.cc @@ -4,11 +4,11 @@ #include "crash-reporter/kernel_warning_collector.h" -#include "base/file_util.h" -#include "base/logging.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" +#include +#include +#include +#include +#include namespace { const char kExecName[] = "kernel-warning"; @@ -34,7 +34,7 @@ bool KernelWarningCollector::LoadKernelWarning(std::string *content, LOG(ERROR) << "Could not open " << kKernelWarningPath; return false; } - /* The signature is in the first line. */ + // The signature is in the first line. std::string::size_type end_position = content->find('\n'); if (end_position == std::string::npos) { LOG(ERROR) << "unexpected kernel warning format"; diff --git a/crash_reporter/kernel_warning_collector.h b/crash_reporter/kernel_warning_collector.h index 7a5041617..2559d72f3 100644 --- a/crash_reporter/kernel_warning_collector.h +++ b/crash_reporter/kernel_warning_collector.h @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef _CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ -#define _CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ +#ifndef CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ +#define CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ + +#include #include // for FRIEND_TEST -#include #include "crash-reporter/crash_collector.h" @@ -28,4 +29,4 @@ class KernelWarningCollector : public CrashCollector { bool LoadKernelWarning(std::string *content, std::string *signature); }; -#endif // _CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ +#endif // CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc index 41dd39046..2e80e4718 100644 --- a/crash_reporter/list_proxies.cc +++ b/crash_reporter/list_proxies.cc @@ -3,21 +3,20 @@ // found in the LICENSE file. #include // for isatty() +#include +#include #include #include -#include -#include - -#include "base/command_line.h" -#include "base/file_util.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/string_tokenizer.h" -#include "base/values.h" -#include "chromeos/dbus/dbus.h" -#include "chromeos/syslog_logging.h" +#include +#include +#include +#include +#include +#include +#include +#include const char kLibCrosProxyResolveSignalInterface[] = "org.chromium.CrashReporterLibcrosProxyResolvedInterface"; diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc index 504f90e11..506552772 100644 --- a/crash_reporter/udev_collector.cc +++ b/crash_reporter/udev_collector.cc @@ -4,12 +4,16 @@ #include "crash-reporter/udev_collector.h" -#include "base/basictypes.h" -#include "base/file_util.h" -#include "base/logging.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "chromeos/process.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include static const char kCollectUdevSignature[] = "crash_reporter-udev-collection"; static const char kGzipPath[] = "/bin/gzip"; diff --git a/crash_reporter/udev_collector.h b/crash_reporter/udev_collector.h index 0976d3e32..e54ce5b43 100644 --- a/crash_reporter/udev_collector.h +++ b/crash_reporter/udev_collector.h @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef _CRASH_REPORTER_UDEV_COLLECTOR_H_ -#define _CRASH_REPORTER_UDEV_COLLECTOR_H_ +#ifndef CRASH_REPORTER_UDEV_COLLECTOR_H_ +#define CRASH_REPORTER_UDEV_COLLECTOR_H_ #include -#include "base/files/file_path.h" +#include +#include // for FRIEND_TEST + #include "crash-reporter/crash_collector.h" -#include "gtest/gtest_prod.h" // for FRIEND_TEST // Udev crash collector. class UdevCollector : public CrashCollector { @@ -22,7 +23,7 @@ class UdevCollector : public CrashCollector { // "ACTION=[action]:KERNEL=[name]:SUBSYSTEM=[subsystem]" // The values don't have to be in any particular order. One or more of them // could be omitted, in which case it would be treated as a wildcard (*). - bool HandleCrash(const std::string &udev_event); + bool HandleCrash(const std::string& udev_event); private: friend class UdevCollectorTest; @@ -33,4 +34,4 @@ class UdevCollector : public CrashCollector { } }; -#endif // _CRASH_REPORTER_UDEV_COLLECTOR_H_ +#endif // CRASH_REPORTER_UDEV_COLLECTOR_H_ diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc index d5d6f29bd..b45fdd8e4 100644 --- a/crash_reporter/udev_collector_test.cc +++ b/crash_reporter/udev_collector_test.cc @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/file_util.h" -#include "base/files/file_enumerator.h" -#include "base/files/scoped_temp_dir.h" -#include "chromeos/test_helpers.h" +#include +#include +#include +#include +#include + #include "crash-reporter/udev_collector.h" -#include "gtest/gtest.h" using base::FilePath; diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc index 8ee83c88b..3c8af6263 100644 --- a/crash_reporter/unclean_shutdown_collector.cc +++ b/crash_reporter/unclean_shutdown_collector.cc @@ -4,8 +4,8 @@ #include "crash-reporter/unclean_shutdown_collector.h" -#include "base/file_util.h" -#include "base/logging.h" +#include +#include static const char kUncleanShutdownFile[] = "/var/lib/crash_reporter/pending_clean_shutdown"; diff --git a/crash_reporter/unclean_shutdown_collector.h b/crash_reporter/unclean_shutdown_collector.h index ec21b7cf8..6324bc397 100644 --- a/crash_reporter/unclean_shutdown_collector.h +++ b/crash_reporter/unclean_shutdown_collector.h @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef _CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_ -#define _CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_ +#ifndef CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_ +#define CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_ #include -#include "base/files/file_path.h" +#include +#include // for FRIEND_TEST + #include "crash-reporter/crash_collector.h" -#include "gtest/gtest_prod.h" // for FRIEND_TEST // Unclean shutdown collector. class UncleanShutdownCollector : public CrashCollector { @@ -43,4 +44,4 @@ class UncleanShutdownCollector : public CrashCollector { base::FilePath powerd_suspended_file_; }; -#endif // _CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_ +#endif // CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_ diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc index a0eec1cc2..d44ec0d96 100644 --- a/crash_reporter/unclean_shutdown_collector_test.cc +++ b/crash_reporter/unclean_shutdown_collector_test.cc @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "crash-reporter/unclean_shutdown_collector.h" + #include -#include "base/file_util.h" -#include "base/strings/string_util.h" -#include "chromeos/syslog_logging.h" -#include "chromeos/test_helpers.h" -#include "crash-reporter/unclean_shutdown_collector.h" -#include "gtest/gtest.h" +#include +#include +#include +#include +#include static int s_crashes = 0; static bool s_metrics = true; diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 3355b0e04..198a43b06 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -12,19 +12,20 @@ #include // For struct passwd. #include // For getpwuid_r, getgrnam_r, WEXITSTATUS. +#include #include #include -#include "base/file_util.h" -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#include "base/stl_util.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "chromeos/process.h" -#include "chromeos/syslog_logging.h" -#include "gflags/gflags.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #pragma GCC diagnostic ignored "-Wstrict-aliasing" DEFINE_bool(core2md_failure, false, "Core2md failure test"); @@ -440,7 +441,7 @@ UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash( // Directory like /tmp/crash_reporter/1234 which contains the // procfs entries and other temporary files used during conversion. - FilePath container_dir(StringPrintf("/tmp/crash_reporter/%d", (int)pid)); + FilePath container_dir(StringPrintf("/tmp/crash_reporter/%d", pid)); // Delete a pre-existing directory from crash reporter that may have // been left around for diagnostics from a failed conversion attempt. // If we don't, existing files can cause forking to fail. @@ -495,19 +496,19 @@ bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes, kernel_supplied_name); } -/* Returns true if the given executable name matches that of Chrome. This - * includes checks for threads that Chrome has renamed. */ +// Returns true if the given executable name matches that of Chrome. This +// includes checks for threads that Chrome has renamed. static bool IsChromeExecName(const std::string &exec) { static const char *kChromeNames[] = { "chrome", - /* These are additional thread names seen in http://crash/ */ + // These are additional thread names seen in http://crash/ "MediaPipeline", - /* These come from the use of base::PlatformThread::SetName() directly */ + // These come from the use of base::PlatformThread::SetName() directly "CrBrowserMain", "CrRendererMain", "CrUtilityMain", "CrPPAPIMain", "CrPPAPIBrokerMain", "CrPluginMain", "CrWorkerMain", "CrGpuMain", "BrokerEvent", "CrVideoRenderer", "CrShutdownDetector", "UsbEventHandler", "CrNaClMain", "CrServiceMain", - /* These thread names come from the use of base::Thread */ + // These thread names come from the use of base::Thread "Gamepad polling thread", "Chrome_InProcGpuThread", "Chrome_DragDropThread", "Renderer::FILE", "VC manager", "VideoCaptureModuleImpl", "JavaBridge", "VideoCaptureManagerThread", @@ -536,29 +537,29 @@ static bool IsChromeExecName(const std::string &exec) { "ServiceProcess_IO", "ServiceProcess_File", "extension_crash_uploader", "gpu-process_crash_uploader", "plugin_crash_uploader", "renderer_crash_uploader", - /* These come from the use of webkit_glue::WebThreadImpl */ + // These come from the use of webkit_glue::WebThreadImpl "Compositor", "Browser Compositor", // "WorkerPool/%d", // not easy to check because of "%d" - /* These come from the use of base::Watchdog */ + // These come from the use of base::Watchdog "Startup watchdog thread Watchdog", "Shutdown watchdog thread Watchdog", - /* These come from the use of AudioDeviceThread::Start */ + // These come from the use of AudioDeviceThread::Start "AudioDevice", "AudioInputDevice", "AudioOutputDevice", - /* These come from the use of MessageLoopFactory::GetMessageLoop */ + // These come from the use of MessageLoopFactory::GetMessageLoop "GpuVideoDecoder", "RtcVideoDecoderThread", "PipelineThread", "AudioDecoderThread", "VideoDecoderThread", - /* These come from the use of MessageLoopFactory::GetMessageLoopProxy */ + // These come from the use of MessageLoopFactory::GetMessageLoopProxy "CaptureVideoDecoderThread", "CaptureVideoDecoder", - /* These come from the use of base::SimpleThread */ + // These come from the use of base::SimpleThread "LocalInputMonitor/%d", // "%d" gets lopped off for kernel-supplied - /* These come from the use of base::DelegateSimpleThread */ + // These come from the use of base::DelegateSimpleThread "ipc_channel_nacl reader thread/%d", "plugin_audio_input_thread/%d", "plugin_audio_thread/%d", - /* These come from the use of base::SequencedWorkerPool */ + // These come from the use of base::SequencedWorkerPool "BrowserBlockingWorker%d/%d", // "%d" gets lopped off for kernel-supplied }; static std::set chrome_names; - /* Initialize a set of chrome names, for efficient lookup */ + // Initialize a set of chrome names, for efficient lookup if (chrome_names.empty()) { for (size_t i = 0; i < arraysize(kChromeNames); i++) { std::string check_name(kChromeNames[i]); diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 6c82fa8dd..3658e5114 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef _CRASH_REPORTER_USER_COLLECTOR_H_ -#define _CRASH_REPORTER_USER_COLLECTOR_H_ +#ifndef CRASH_REPORTER_USER_COLLECTOR_H_ +#define CRASH_REPORTER_USER_COLLECTOR_H_ #include #include -#include "base/files/file_path.h" +#include +#include // for FRIEND_TEST + #include "crash-reporter/crash_collector.h" -#include "gtest/gtest_prod.h" // for FRIEND_TEST class SystemLogging; @@ -178,4 +179,4 @@ class UserCollector : public CrashCollector { static const char *kGroupId; }; -#endif // _CRASH_REPORTER_USER_COLLECTOR_H_ +#endif // CRASH_REPORTER_USER_COLLECTOR_H_ diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index eeaf070b7..242c117fe 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -2,17 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "crash-reporter/user_collector.h" + #include #include #include -#include "base/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/strings/string_split.h" -#include "chromeos/syslog_logging.h" -#include "chromeos/test_helpers.h" -#include "crash-reporter/user_collector.h" -#include "gtest/gtest.h" +#include +#include +#include +#include +#include +#include static int s_crashes = 0; static bool s_metrics = false; diff --git a/crash_reporter/warn_collector_test.c b/crash_reporter/warn_collector_test.c index 76707b200..7e25d01fa 100644 --- a/crash_reporter/warn_collector_test.c +++ b/crash_reporter/warn_collector_test.c @@ -8,8 +8,7 @@ */ #include -int main(int ac, char **av) -{ +int main(int ac, char **av) { int status = system("exec \"${SRC}\"/warn_collector_test.sh"); return status < 0 ? EXIT_FAILURE : WEXITSTATUS(status); } From 04661e3375a64820d81915fe382aef1680a97cc3 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Wed, 18 Jun 2014 10:47:48 -0700 Subject: [PATCH 179/225] crash-reporter: Create a standalone ebuild for system_api system_api is required by a lot of platform2 packages. We need it to be in a standalone ebuild in order for other packages to depend on it. This package installs the common headers and compiles the protobufs into static libraries. BUG=chromium:386223 TEST=FEATURES=test emerge-amd64-generic platform2. TEST=trybot run on daisy, link, duck, x86-mario. TEST=trybot run on lumpy-incremental-release. CQ-DEPEND=CL:204594 Change-Id: I235b5baf03a4497352f7d53ab68326ddb4e9676a Reviewed-on: https://chromium-review.googlesource.com/204581 Reviewed-by: Mike Frysinger Tested-by: Bertrand Simonnet Commit-Queue: Bertrand Simonnet --- crash_reporter/crash-reporter.gyp | 3 --- 1 file changed, 3 deletions(-) diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index dfa95ec8d..c3135b97d 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -35,9 +35,6 @@ ], }, }, - 'dependencies': [ - '<(platform_root)/system_api/system_api.gyp:system_api-headers', - ], 'sources': [ 'chrome_collector.cc', 'crash_collector.cc', From 9769dc865ba50c09e7365cf3a8d35bb7a6bdfb7b Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Fri, 20 Jun 2014 15:28:28 -0700 Subject: [PATCH 180/225] Update gyp files to depend on metrics' standalone ebuild. Metrics is now installed by chromeos-base/metrics. Platform2 packages should depend on it through the variable:deps mechanism instead of depending directly on metrics' gyp file. BUG=chromium:387923 TEST=Build platform2. metrics gets installed and the tests succeed. TEST=Trybot run on daisy-release, lumpy-incremental-paladin, link-release, duck-release, x86-mario-release. CQ-DEPEND=CL:205312 Change-Id: I07607d5fa574ce3c273fc4189590453edbee2aa4 Reviewed-on: https://chromium-review.googlesource.com/205299 Reviewed-by: Mike Frysinger Tested-by: Bertrand Simonnet Commit-Queue: Bertrand Simonnet --- crash_reporter/crash-reporter.gyp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index c3135b97d..4f9dbe8ee 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -52,11 +52,11 @@ 'deps': [ 'dbus-1', 'dbus-glib-1', + 'libmetrics-<(libbase_ver)', ], }, 'dependencies': [ 'libcrash', - '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', ], 'sources': [ 'crash_reporter.cc', @@ -81,15 +81,15 @@ 'type': 'executable', 'variables': { 'lexer_out_dir': 'crash-reporter', + 'deps': [ + 'libmetrics-<(libbase_ver)', + ], }, 'link_settings': { 'libraries': [ '-lfl', ], }, - 'dependencies': [ - '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', - ], 'sources': [ 'warn_collector.l', ], From ae88303ac0a592c9cf4c91ecf6f19d586e0c3243 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Mon, 7 Jul 2014 23:40:57 -0700 Subject: [PATCH 181/225] platform2: Remove redundant LICENSE files. There is a common LICENSE file at the top level of the platform2 repository. The LICENSE files in individual packages are no longer necessary and thus removed by this CL. BUG=None TEST=Trybot runs on paladin, release, and chromiumos-sdk builders. Change-Id: If9e26ef5e40c8e3ada4d7d29575bcdf61edfb39d Reviewed-on: https://chromium-review.googlesource.com/206947 Reviewed-by: Gaurav Shah Commit-Queue: Ben Chan Tested-by: Ben Chan --- crash_reporter/LICENSE | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 crash_reporter/LICENSE diff --git a/crash_reporter/LICENSE b/crash_reporter/LICENSE deleted file mode 100644 index d25149653..000000000 --- a/crash_reporter/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From f7ad519b3d179a7d766adcdd6834994f05cc9cf4 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Mon, 7 Jul 2014 16:59:48 -0700 Subject: [PATCH 182/225] Remove the dependency of platform.gyp to crash-reporter We are creating a standalone ebuild for crash-reporter. platform.gyp should not depend on crash-reporter.gyp as crash-reporter will be build and installed by chromeos-base/crash-reporter. BUG=chromium:391999 TEST=FEATURES=test emerge-amd64-generic platform2. TEST=FEATURES=test emerge-amd64-generic crash-reporter. CQ-DEPEND=CL:206918 Change-Id: I726ed0c847e361b7f03c2ef82fbb8410f40b6c80 Reviewed-on: https://chromium-review.googlesource.com/206919 Reviewed-by: Gaurav Shah Tested-by: Bertrand Simonnet Commit-Queue: Bertrand Simonnet --- crash_reporter/crash-reporter.gyp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index 4f9dbe8ee..79faf97e5 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -93,7 +93,7 @@ 'sources': [ 'warn_collector.l', ], - 'includes': ['../../platform2/common-mk/lex.gypi'], + 'includes': ['../common-mk/lex.gypi'], }, ], 'conditions': [ @@ -102,7 +102,7 @@ { 'target_name': 'chrome_collector_test', 'type': 'executable', - 'includes': ['../../platform2/common-mk/common_test.gypi'], + 'includes': ['../common-mk/common_test.gypi'], 'dependencies': ['libcrash'], 'sources': [ 'chrome_collector_test.cc', @@ -111,7 +111,7 @@ { 'target_name': 'crash_collector_test', 'type': 'executable', - 'includes': ['../../platform2/common-mk/common_test.gypi'], + 'includes': ['../common-mk/common_test.gypi'], 'dependencies': ['libcrash'], 'sources': [ 'crash_collector_test.cc', @@ -120,7 +120,7 @@ { 'target_name': 'kernel_collector_test', 'type': 'executable', - 'includes': ['../../platform2/common-mk/common_test.gypi'], + 'includes': ['../common-mk/common_test.gypi'], 'dependencies': ['libcrash'], 'sources': [ 'kernel_collector_test.cc', @@ -129,7 +129,7 @@ { 'target_name': 'udev_collector_test', 'type': 'executable', - 'includes': ['../../platform2/common-mk/common_test.gypi'], + 'includes': ['../common-mk/common_test.gypi'], 'dependencies': ['libcrash'], 'sources': [ 'udev_collector_test.cc', @@ -138,7 +138,7 @@ { 'target_name': 'unclean_shutdown_collector_test', 'type': 'executable', - 'includes': ['../../platform2/common-mk/common_test.gypi'], + 'includes': ['../common-mk/common_test.gypi'], 'dependencies': ['libcrash'], 'sources': [ 'unclean_shutdown_collector_test.cc', @@ -147,7 +147,7 @@ { 'target_name': 'user_collector_test', 'type': 'executable', - 'includes': ['../../platform2/common-mk/common_test.gypi'], + 'includes': ['../common-mk/common_test.gypi'], 'dependencies': ['libcrash'], 'sources': [ 'user_collector_test.cc', From 120c675df0836290d629764d06f1eb4ba49d7740 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Tue, 22 Jul 2014 21:06:09 -0700 Subject: [PATCH 183/225] crash-reporter: Add MIPS support for KernelCollector. BUG=chromium:396465 TEST=`FEATURES=test emerge-x86-generic crash-reporter` TEST=`FEATURES=test emerge-amd64-generic crash-reporter` TEST=`emerge-arm-generic crash-reporter` TEST=`emerge-mipsel-o32-generic-generic crash-reporter` Change-Id: I270b56d3f0b6941c97ebec6e736230701e5c3e1f Reviewed-on: https://chromium-review.googlesource.com/209485 Reviewed-by: Mike Frysinger Commit-Queue: Ben Chan Tested-by: Ben Chan --- crash_reporter/kernel_collector.cc | 8 ++++ crash_reporter/kernel_collector.h | 1 + crash_reporter/kernel_collector_test.cc | 58 +++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 2e93f2b98..86063691f 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -36,6 +36,8 @@ static const std::string kTimestampRegex("^<.*>\\[\\s*(\\d+\\.\\d+)\\]"); // // For ARM we see: // "<5>[ 39.458982] PC is at write_breakme+0xd0/0x1b4" +// For MIPS we see: +// "<5>[ 3378.552000] epc : 804010f0 lkdtm_do_action+0x68/0x3f8" // For x86: // "<0>[ 37.474699] EIP: [<790ed488>] write_breakme+0x80/0x108 // SS:ESP 0068:e9dd3efc" @@ -43,6 +45,7 @@ static const std::string kTimestampRegex("^<.*>\\[\\s*(\\d+\\.\\d+)\\]"); static const char *s_pc_regex[] = { 0, " PC is at ([^\\+ ]+).*", + " epc\\s+:\\s+\\S+\\s+([^\\+ ]+).*", // MIPS has an exception program counter " EIP: \\[<.*>\\] ([^\\+ ]+).*", // X86 uses EIP for the program counter " RIP \\[<.*>\\] ([^\\+ ]+).*", // X86_64 uses RIP for the program counter }; @@ -277,6 +280,9 @@ void KernelCollector::ProcessStackTrace( // <4>[ 3498.731164] [] ? (function_name+0x20/0x2c) from // [] (foo_bar+0xdc/0x1bc) // + // For MIPS: + // <5>[ 3378.656000] [<804010f0>] lkdtm_do_action+0x68/0x3f8 + // // For X86: // <4>[ 6066.849504] [<7937bcee>] ? function_name+0x66/0x6c // @@ -353,6 +359,8 @@ void KernelCollector::ProcessStackTrace( enum KernelCollector::ArchKind KernelCollector::GetCompilerArch(void) { #if defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY) return archArm; +#elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS_FAMILY) + return archMips; #elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_64) return archX86_64; #elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY) diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index 8f52e440e..a68144a04 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -21,6 +21,7 @@ class KernelCollector : public CrashCollector { enum ArchKind { archUnknown, archArm, + archMips, archX86, archX86_64, diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 2644891ef..9e00d3286 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -367,6 +367,64 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignatureARM) { ComputeKernelStackSignatureCommon(); } +TEST_F(KernelCollectorTest, ComputeKernelStackSignatureMIPS) { + const char kBugToPanic[] = + "<5>[ 3378.472000] lkdtm: Performing direct entry BUG\n" + "<5>[ 3378.476000] Kernel bug detected[#1]:\n" + "<5>[ 3378.484000] CPU: 0 PID: 185 Comm: dash Not tainted 3.14.0 #1\n" + "<5>[ 3378.488000] task: 8fed5220 ti: 8ec4a000 task.ti: 8ec4a000\n" + "<5>[ 3378.496000] $ 0 : 00000000 804018b8 804010f0 7785b507\n" + "<5>[ 3378.500000] $ 4 : 8061ab64 81204478 81205b20 00000000\n" + "<5>[ 3378.508000] $ 8 : 80830000 20746365 72746e65 55422079\n" + "<5>[ 3378.512000] $12 : 8ec4be94 000000fc 00000000 00000048\n" + "<5>[ 3378.520000] $16 : 00000004 8ef54000 80710000 00000002\n" + "<5>[ 3378.528000] $20 : 7765b6d4 00000004 7fffffff 00000002\n" + "<5>[ 3378.532000] $24 : 00000001 803dc0dc \n" + "<5>[ 3378.540000] $28 : 8ec4a000 8ec4be20 7775438d 804018b8\n" + "<5>[ 3378.544000] Hi : 00000000\n" + "<5>[ 3378.548000] Lo : 49bf8080\n" + "<5>[ 3378.552000] epc : 804010f0 lkdtm_do_action+0x68/0x3f8\n" + "<5>[ 3378.560000] Not tainted\n" + "<5>[ 3378.564000] ra : 804018b8 direct_entry+0x110/0x154\n" + "<5>[ 3378.568000] Status: 3100dc03 KERNEL EXL IE \n" + "<5>[ 3378.572000] Cause : 10800024\n" + "<5>[ 3378.576000] PrId : 0001a120 (MIPS interAptiv (multi))\n" + "<5>[ 3378.580000] Modules linked in: uinput cfg80211 nf_conntrack_ipv6 " + "nf_defrag_ipv6 ip6table_filter ip6_tables pcnet32 mii fuse " + "ppp_async ppp_generic slhc tun\n" + "<5>[ 3378.600000] Process dash (pid: 185, threadinfo=8ec4a000, " + "task=8fed5220, tls=77632490)\n" + "<5>[ 3378.608000] Stack : 00000006 ffffff9c 00000000 00000000 00000000 " + "00000000 8083454a 00000022\n" + "<5> 7765baa1 00001fee 80710000 8ef54000 8ec4bf08 00000002 " + "7765b6d4 00000004\n" + "<5> 7fffffff 00000002 7775438d 805e5158 7fffffff 00000002 " + "00000000 7785b507\n" + "<5> 806a96bc 00000004 8ef54000 8ec4bf08 00000002 804018b8 " + "80710000 806a98bc\n" + "<5> 00000002 00000020 00000004 8d515600 77756450 00000004 " + "8ec4bf08 802377e4\n" + "<5> ...\n" + "<5>[ 3378.652000] Call Trace:\n" + "<5>[ 3378.656000] [<804010f0>] lkdtm_do_action+0x68/0x3f8\n" + "<5>[ 3378.660000] [<804018b8>] direct_entry+0x110/0x154\n" + "<5>[ 3378.664000] [<802377e4>] vfs_write+0xe0/0x1bc\n" + "<5>[ 3378.672000] [<80237f90>] SyS_write+0x78/0xf8\n" + "<5>[ 3378.676000] [<80111888>] handle_sys+0x128/0x14c\n" + "<5>[ 3378.680000] \n" + "<5>[ 3378.684000] \n" + "<5>Code: 3c04806b 0c1793aa 248494f0 <000c000d> 3c04806b 248494fc " + "0c04cc7f 2405017a 08100514 \n" + "<5>[ 3378.696000] ---[ end trace 75067432f24bbc93 ]---\n"; + std::string signature; + + collector_.SetArch(KernelCollector::archMips); + EXPECT_TRUE( + collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false)); + EXPECT_EQ("kernel-lkdtm_do_action-5E600A6B", signature); + + ComputeKernelStackSignatureCommon(); +} TEST_F(KernelCollectorTest, ComputeKernelStackSignatureX86) { const char kBugToPanic[] = From 3c6b82c4c73f9ac39a71809f01af6f3b75339606 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Wed, 23 Jul 2014 14:52:14 -0700 Subject: [PATCH 184/225] crash-reporter: Fix C++ style issues in KernelCollector. BUG=None TEST=`FEATURES=test emerge-$BOARD crash-reporter` Change-Id: Ib33a23059878380425f6eb79385dc67141ad0f77 Reviewed-on: https://chromium-review.googlesource.com/209746 Reviewed-by: Alex Vakulenko Commit-Queue: Ben Chan Tested-by: Ben Chan --- crash_reporter/crash_reporter.cc | 2 +- crash_reporter/kernel_collector.cc | 90 ++++++++++++------------- crash_reporter/kernel_collector.h | 28 ++++---- crash_reporter/kernel_collector_test.cc | 12 ++-- 4 files changed, 65 insertions(+), 67 deletions(-) diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 8aafc4677..2a74f1318 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -130,7 +130,7 @@ static int Initialize(KernelCollector *kernel_collector, bool was_kernel_crash = false; bool was_unclean_shutdown = false; kernel_collector->Enable(); - if (kernel_collector->IsEnabled()) { + if (kernel_collector->is_enabled()) { was_kernel_crash = kernel_collector->Collect(); } diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 86063691f..08ad85b83 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -11,23 +11,27 @@ #include #include -static const char kDefaultKernelStackSignature[] = - "kernel-UnspecifiedStackSignature"; -static const char kDumpPath[] = "/dev/pstore"; -static const char kDumpFormat[] = "dmesg-ramoops-%zu"; -static const char kKernelExecName[] = "kernel"; +using base::FilePath; +using base::StringPrintf; + +namespace { + +const char kDefaultKernelStackSignature[] = "kernel-UnspecifiedStackSignature"; +const char kDumpPath[] = "/dev/pstore"; +const char kDumpFormat[] = "dmesg-ramoops-%zu"; +const char kKernelExecName[] = "kernel"; // Maximum number of records to examine in the kDumpPath. -static const size_t kMaxDumpRecords = 100; +const size_t kMaxDumpRecords = 100; const pid_t kKernelPid = 0; -static const char kKernelSignatureKey[] = "sig"; +const char kKernelSignatureKey[] = "sig"; // Byte length of maximum human readable portion of a kernel crash signature. -static const int kMaxHumanStringLength = 40; +const int kMaxHumanStringLength = 40; const uid_t kRootUid = 0; // Time in seconds from the final kernel log message for a call stack // to count towards the signature of the kcrash. -static const int kSignatureTimestampWindow = 2; +const int kSignatureTimestampWindow = 2; // Kernel log timestamp regular expression. -static const std::string kTimestampRegex("^<.*>\\[\\s*(\\d+\\.\\d+)\\]"); +const char kTimestampRegex[] = "^<.*>\\[\\s*(\\d+\\.\\d+)\\]"; // // These regular expressions enable to us capture the PC in a backtrace. @@ -42,7 +46,7 @@ static const std::string kTimestampRegex("^<.*>\\[\\s*(\\d+\\.\\d+)\\]"); // "<0>[ 37.474699] EIP: [<790ed488>] write_breakme+0x80/0x108 // SS:ESP 0068:e9dd3efc" // -static const char *s_pc_regex[] = { +const char* const kPCRegex[] = { 0, " PC is at ([^\\+ ]+).*", " epc\\s+:\\s+\\S+\\s+([^\\+ ]+).*", // MIPS has an exception program counter @@ -50,18 +54,17 @@ static const char *s_pc_regex[] = { " RIP \\[<.*>\\] ([^\\+ ]+).*", // X86_64 uses RIP for the program counter }; -using base::FilePath; -using base::StringPrintf; - -COMPILE_ASSERT(arraysize(s_pc_regex) == KernelCollector::archCount, +COMPILE_ASSERT(arraysize(kPCRegex) == KernelCollector::kArchCount, missing_arch_pc_regexp); +} // namespace + KernelCollector::KernelCollector() : is_enabled_(false), ramoops_dump_path_(kDumpPath), - records_(0) { - // We expect crash dumps in the format of the architecture we are built for. - arch_ = GetCompilerArch(); + records_(0), + // We expect crash dumps in the format of architecture we are built for. + arch_(GetCompilerArch()) { } KernelCollector::~KernelCollector() { @@ -80,8 +83,9 @@ bool KernelCollector::ReadRecordToString(std::string *contents, // Ramoops appends a header to a crash which contains ==== followed by a // timestamp. Ignore the header. - pcrecpp::RE record_re("====\\d+\\.\\d+\n(.*)", - pcrecpp::RE_Options().set_multiline(true).set_dotall(true)); + pcrecpp::RE record_re( + "====\\d+\\.\\d+\n(.*)", + pcrecpp::RE_Options().set_multiline(true).set_dotall(true)); FilePath ramoops_record; GetRamoopsRecordPath(&ramoops_record, current_record); @@ -232,17 +236,16 @@ void KernelCollector::StripSensitiveData(std::string *kernel_dump) { } bool KernelCollector::Enable() { - if (arch_ == archUnknown || arch_ >= archCount || - s_pc_regex[arch_] == NULL) { + if (arch_ == kArchUnknown || arch_ >= kArchCount || kPCRegex[arch_] == NULL) { LOG(WARNING) << "KernelCollector does not understand this architecture"; return false; - } else { - FilePath ramoops_record; - GetRamoopsRecordPath(&ramoops_record, 0); - if (!base::PathExists(ramoops_record)) { - LOG(WARNING) << "Kernel does not support crash dumping"; - return false; - } + } + + FilePath ramoops_record; + GetRamoopsRecordPath(&ramoops_record, 0); + if (!base::PathExists(ramoops_record)) { + LOG(WARNING) << "Kernel does not support crash dumping"; + return false; } // To enable crashes, we will eventually need to set @@ -270,7 +273,7 @@ void KernelCollector::ProcessStackTrace( float *last_stack_timestamp, bool *is_watchdog_crash) { pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE()); - pcrecpp::RE stack_trace_start_re(kTimestampRegex + + pcrecpp::RE stack_trace_start_re(std::string(kTimestampRegex) + " (Call Trace|Backtrace):$"); // Match lines such as the following and grab out "function_name". @@ -286,7 +289,7 @@ void KernelCollector::ProcessStackTrace( // For X86: // <4>[ 6066.849504] [<7937bcee>] ? function_name+0x66/0x6c // - pcrecpp::RE stack_entry_re(kTimestampRegex + + pcrecpp::RE stack_entry_re(std::string(kTimestampRegex) + "\\s+\\[<[[:xdigit:]]+>\\]" // Matches " [<7937bcee>]" "([\\s\\?(]+)" // Matches " ? (" (ARM) or " ? " (X86) "([^\\+ )]+)"); // Matches until delimiter reached @@ -356,24 +359,21 @@ void KernelCollector::ProcessStackTrace( } } -enum KernelCollector::ArchKind KernelCollector::GetCompilerArch(void) { +// static +KernelCollector::ArchKind KernelCollector::GetCompilerArch() { #if defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY) - return archArm; + return kArchArm; #elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS_FAMILY) - return archMips; + return kArchMips; #elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_64) - return archX86_64; + return kArchX86_64; #elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY) - return archX86; + return kArchX86; #else - return archUnknown; + return kArchUnknown; #endif } -void KernelCollector::SetArch(enum ArchKind arch) { - arch_ = arch; -} - bool KernelCollector::FindCrashingFunction( pcrecpp::StringPiece kernel_dump, bool print_diagnostics, @@ -382,7 +382,7 @@ bool KernelCollector::FindCrashingFunction( float timestamp = 0; // Use the correct regex for this architecture. - pcrecpp::RE eip_re(kTimestampRegex + s_pc_regex[arch_], + pcrecpp::RE eip_re(std::string(kTimestampRegex) + kPCRegex[arch_], pcrecpp::MULTILINE()); while (eip_re.FindAndConsume(&kernel_dump, ×tamp, crashing_function)) { @@ -417,7 +417,7 @@ bool KernelCollector::FindPanicMessage(pcrecpp::StringPiece kernel_dump, std::string *panic_message) { // Match lines such as the following and grab out "Fatal exception" // <0>[ 342.841135] Kernel panic - not syncing: Fatal exception - pcrecpp::RE kernel_panic_re(kTimestampRegex + + pcrecpp::RE kernel_panic_re(std::string(kTimestampRegex) + " Kernel panic[^\\:]*\\:\\s*(.*)", pcrecpp::MULTILINE()); float timestamp = 0; @@ -524,9 +524,7 @@ bool KernelCollector::Collect() { } std::string dump_basename = - FormatDumpBasename(kKernelExecName, - time(NULL), - kKernelPid); + FormatDumpBasename(kKernelExecName, time(NULL), kKernelPid); FilePath kernel_crash_path = root_crash_directory.Append( StringPrintf("%s.kcrash", dump_basename.c_str())); diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index a68144a04..5f21747a3 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -19,13 +19,13 @@ class KernelCollector : public CrashCollector { public: // Enumeration to specify architecture type. enum ArchKind { - archUnknown, - archArm, - archMips, - archX86, - archX86_64, + kArchUnknown, + kArchArm, + kArchMips, + kArchX86, + kArchX86_64, - archCount // Number of architectures. + kArchCount // Number of architectures. }; KernelCollector(); @@ -38,9 +38,7 @@ class KernelCollector : public CrashCollector { bool Enable(); // Returns true if the kernel collection currently enabled. - bool IsEnabled() { - return is_enabled_; - } + bool is_enabled() const { return is_enabled_; } // Collect any preserved kernel crash dump. Returns true if there was // a dump (even if there were problems storing the dump), false otherwise. @@ -52,8 +50,8 @@ class KernelCollector : public CrashCollector { bool print_diagnostics); // Set the architecture of the crash dumps we are looking at. - void SetArch(enum ArchKind arch); - enum ArchKind GetArch() { return arch_; } + void set_arch(ArchKind arch) { arch_ = arch; } + ArchKind arch() const { return arch_; } private: friend class KernelCollectorTest; @@ -94,15 +92,17 @@ class KernelCollector : public CrashCollector { bool print_diagnostics, std::string *panic_message); - // Returns the architecture kind for which we are built - enum ArchKind. - enum ArchKind GetCompilerArch(void); + // Returns the architecture kind for which we are built. + static ArchKind GetCompilerArch(); bool is_enabled_; base::FilePath ramoops_dump_path_; size_t records_; // The architecture of kernel dump strings we are working with. - enum ArchKind arch_; + ArchKind arch_; + + DISALLOW_COPY_AND_ASSIGN(KernelCollector); }; #endif // CRASH_REPORTER_KERNEL_COLLECTOR_H_ diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 9e00d3286..ec7ad8e11 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -70,7 +70,7 @@ class KernelCollectorTest : public ::testing::Test { TEST_F(KernelCollectorTest, ComputeKernelStackSignatureBase) { // Make sure the normal build architecture is detected - EXPECT_TRUE(collector_.GetArch() != KernelCollector::archUnknown); + EXPECT_NE(KernelCollector::kArchUnknown, collector_.arch()); } TEST_F(KernelCollectorTest, LoadPreservedDump) { @@ -91,7 +91,7 @@ TEST_F(KernelCollectorTest, LoadPreservedDump) { TEST_F(KernelCollectorTest, EnableMissingKernel) { ASSERT_FALSE(collector_.Enable()); - ASSERT_FALSE(collector_.IsEnabled()); + ASSERT_FALSE(collector_.is_enabled()); ASSERT_TRUE(FindLog( "Kernel does not support crash dumping")); ASSERT_EQ(s_crashes, 0); @@ -100,7 +100,7 @@ TEST_F(KernelCollectorTest, EnableMissingKernel) { TEST_F(KernelCollectorTest, EnableOK) { WriteStringToFile(kcrash_file(), ""); ASSERT_TRUE(collector_.Enable()); - ASSERT_TRUE(collector_.IsEnabled()); + ASSERT_TRUE(collector_.is_enabled()); ASSERT_TRUE(FindLog("Enabling kernel crash handling")); ASSERT_EQ(s_crashes, 0); } @@ -359,7 +359,7 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignatureARM) { "[] (proc_reg_write+0x88/0x9c)\n"; std::string signature; - collector_.SetArch(KernelCollector::archArm); + collector_.set_arch(KernelCollector::kArchArm); EXPECT_TRUE( collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false)); EXPECT_EQ("kernel-write_breakme-97D3E92F", signature); @@ -418,7 +418,7 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignatureMIPS) { "<5>[ 3378.696000] ---[ end trace 75067432f24bbc93 ]---\n"; std::string signature; - collector_.SetArch(KernelCollector::archMips); + collector_.set_arch(KernelCollector::kArchMips); EXPECT_TRUE( collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false)); EXPECT_EQ("kernel-lkdtm_do_action-5E600A6B", signature); @@ -443,7 +443,7 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignatureX86) { "<4>[ 6066.950208] [<7901b260>] no_context+0x10d/0x117\n"; std::string signature; - collector_.SetArch(KernelCollector::archX86); + collector_.set_arch(KernelCollector::kArchX86); EXPECT_TRUE( collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false)); EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-DE253569", signature); From 7589aff9f0410512168914bda2d7640649858066 Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Wed, 30 Jul 2014 10:07:35 -0700 Subject: [PATCH 185/225] crash-reporter: fix linter issues Fixed various issues reported by cpplint.py when run on src/platform2/crash-reporter. BUG=None TEST=FEATURES=test emerge-link crash-reporter cpplint.py `find crash-reporter/* | grep "\.cc\|\.h"` Change-Id: If874b3dbf539d2dd22cf740a0d3523225d1c61a7 Reviewed-on: https://chromium-review.googlesource.com/210418 Tested-by: Alex Vakulenko Reviewed-by: Alex Deymo Commit-Queue: Alex Vakulenko --- crash_reporter/crash_collector.cc | 4 ++-- crash_reporter/list_proxies.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 152aec323..9b300741b 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -6,12 +6,12 @@ #include #include // For file creation modes. +#define __STDC_FORMAT_MACROS // PRId64 +#include #include // For struct passwd. #include // for mode_t. #include // For waitpid. #include // For execv and fork. -#define __STDC_FORMAT_MACROS // PRId64 -#include #include #include diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc index 2e80e4718..95ad0eb66 100644 --- a/crash_reporter/list_proxies.cc +++ b/crash_reporter/list_proxies.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include // for isatty() #include #include +#include // for isatty() #include #include From f84ea21aab3c1c6ef1f358534e5c437878675d74 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Wed, 6 Aug 2014 17:27:48 -0700 Subject: [PATCH 186/225] crash-reporter: Use integer types from stdint.h This CL replaces the deprecated int* and uint* types from 'base/basictypes.h' with the int*_t and uint*_t types from 'stdint.h'. BUG=chromium:401356 TEST=`FEATURES=test emerge-$BOARD platform2` Change-Id: I33086d64b33ac7d58b578705e95da31e6d6fb5fd Reviewed-on: https://chromium-review.googlesource.com/211285 Reviewed-by: Mike Frysinger Tested-by: Ben Chan Commit-Queue: Ben Chan --- crash_reporter/chrome_collector.cc | 5 +++-- crash_reporter/crash_collector.cc | 2 +- crash_reporter/user_collector.cc | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 91a926bf8..c94ef6415 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -171,8 +172,8 @@ bool ChromeCollector::HandleCrash(const FilePath &file_path, } if (GetAdditionalLogs(log_path)) { - int64 minidump_size = 0; - int64 log_size = 0; + int64_t minidump_size = 0; + int64_t log_size = 0; if (base::GetFileSize(minidump_path, &minidump_size) && base::GetFileSize(log_path, &log_size) && minidump_size > 0 && log_size > 0 && diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 9b300741b..d612ec7df 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -509,7 +509,7 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, if ((i = contents.find("CHROMEOS_RELEASE_VERSION")) != contents.end()) { version = i->second; } - int64 payload_size = -1; + int64_t payload_size = -1; base::GetFileSize(FilePath(payload_path), &payload_size); std::string meta_data = StringPrintf("%sexec_name=%s\n" "ver=%s\n" diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 198a43b06..f387bd0ff 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -10,6 +10,7 @@ #include // For struct group. #include #include // For struct passwd. +#include #include // For getpwuid_r, getgrnam_r, WEXITSTATUS. #include @@ -248,7 +249,7 @@ bool UserCollector::CopyOffProcFiles(pid_t pid, bool UserCollector::ValidateProcFiles(const FilePath &container_dir) const { // Check if the maps file is empty, which could be due to the crashed // process being reaped by the kernel before finishing a core dump. - int64 file_size = 0; + int64_t file_size = 0; if (!base::GetFileSize(container_dir.Append("maps"), &file_size)) { LOG(ERROR) << "Could not get the size of maps file"; return false; From eaf060c265f4cd84eb718b625b3670987e155a1b Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Fri, 8 Aug 2014 09:36:48 -0700 Subject: [PATCH 187/225] platform2: move --std=gnu++11 flag to common-mk/common.gypi A number of platform2 projects enabled C++11 features individually in their .GYP files. Removed this flag from each of the projects and added it to common.gypi for everyone to share. BUG=None TEST=Compile various platform2 targets to make sure they still compile. Change-Id: I4136573ec5dcc988df2826056c8dae8847725504 Reviewed-on: https://chromium-review.googlesource.com/211534 Reviewed-by: Alex Vakulenko Commit-Queue: Alex Vakulenko Tested-by: Alex Vakulenko --- crash_reporter/crash_collector.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index d612ec7df..f1dbb58c5 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -514,7 +514,7 @@ void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, std::string meta_data = StringPrintf("%sexec_name=%s\n" "ver=%s\n" "payload=%s\n" - "payload_size=%"PRId64"\n" + "payload_size=%" PRId64 "\n" "done=1\n", extra_metadata_.c_str(), exec_name.c_str(), From efec0b3625f447995d2ed79b946d2929039313c9 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Tue, 12 Aug 2014 08:52:11 -0700 Subject: [PATCH 188/225] crash-reporter: Use C++11 override specifier. Also remove some unnecessary 'void' arguments. BUG=None TEST=`FEATURES=test emerge-$BOARD crash-reporter` Change-Id: I0039aec93aae14cb238f284fda756b631f9c1587 Reviewed-on: https://chromium-review.googlesource.com/211959 Reviewed-by: Mike Frysinger Commit-Queue: Ben Chan Tested-by: Ben Chan --- crash_reporter/chrome_collector.h | 2 +- crash_reporter/crash_collector.cc | 4 ++-- crash_reporter/crash_collector.h | 4 ++-- crash_reporter/kernel_collector.h | 4 ++-- crash_reporter/kernel_warning_collector.h | 2 +- crash_reporter/list_proxies.cc | 2 +- crash_reporter/udev_collector.h | 2 +- crash_reporter/unclean_shutdown_collector.h | 2 +- crash_reporter/user_collector.h | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h index 3586d52ec..90e5e3879 100644 --- a/crash_reporter/chrome_collector.h +++ b/crash_reporter/chrome_collector.h @@ -18,7 +18,7 @@ class SystemLogging; class ChromeCollector : public CrashCollector { public: ChromeCollector(); - virtual ~ChromeCollector(); + ~ChromeCollector() override; // Magic string to let Chrome know the crash report succeeded. static const char kSuccessMagic[]; diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index f1dbb58c5..966970b3e 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -154,7 +154,7 @@ const char *GetGErrorMessage(const GError *error) { } -GHashTable *CrashCollector::GetActiveUserSessions(void) { +GHashTable *CrashCollector::GetActiveUserSessions() { GHashTable *active_sessions = NULL; chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); @@ -189,7 +189,7 @@ GHashTable *CrashCollector::GetActiveUserSessions(void) { return active_sessions; } -FilePath CrashCollector::GetUserCrashPath(void) { +FilePath CrashCollector::GetUserCrashPath() { // In this multiprofile world, there is no one-specific user dir anymore. // Ask the session manager for the active ones, then just run with the // first result we get back. diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 0e786618d..49aba120c 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -76,8 +76,8 @@ class CrashCollector { forced_crash_directory_ = forced_directory; } - virtual GHashTable *GetActiveUserSessions(void); - base::FilePath GetUserCrashPath(void); + virtual GHashTable *GetActiveUserSessions(); + base::FilePath GetUserCrashPath(); base::FilePath GetCrashDirectoryInfo(uid_t process_euid, uid_t default_user_id, gid_t default_user_group, diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index 5f21747a3..860256430 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -30,7 +30,7 @@ class KernelCollector : public CrashCollector { KernelCollector(); - virtual ~KernelCollector(); + ~KernelCollector() override; void OverridePreservedDumpPath(const base::FilePath &file_path); @@ -65,7 +65,7 @@ class KernelCollector : public CrashCollector { void StripSensitiveData(std::string *kernel_dump); void GetRamoopsRecordPath(base::FilePath *path, size_t record); - virtual bool LoadParameters(); + bool LoadParameters(); bool HasMoreRecords(); // Read a record to string, modified from file_utils since that didn't diff --git a/crash_reporter/kernel_warning_collector.h b/crash_reporter/kernel_warning_collector.h index 2559d72f3..0b2002735 100644 --- a/crash_reporter/kernel_warning_collector.h +++ b/crash_reporter/kernel_warning_collector.h @@ -16,7 +16,7 @@ class KernelWarningCollector : public CrashCollector { public: KernelWarningCollector(); - virtual ~KernelWarningCollector(); + ~KernelWarningCollector() override; // Collects warning. bool Collect(); diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc index 95ad0eb66..17793af3e 100644 --- a/crash_reporter/list_proxies.cc +++ b/crash_reporter/list_proxies.cc @@ -107,7 +107,7 @@ class BrowserProxyResolvedSignalWatcher : public chromeos::dbus::SignalWatcher { std::deque *proxies) : main_loop_(main_loop), proxies_(proxies) { } - virtual void OnSignal(DBusMessage *message) { + void OnSignal(DBusMessage *message) override { // Get args char *source_url = NULL; char *proxy_list = NULL; diff --git a/crash_reporter/udev_collector.h b/crash_reporter/udev_collector.h index e54ce5b43..2b9bd34b9 100644 --- a/crash_reporter/udev_collector.h +++ b/crash_reporter/udev_collector.h @@ -17,7 +17,7 @@ class UdevCollector : public CrashCollector { public: UdevCollector(); - virtual ~UdevCollector(); + ~UdevCollector() override; // The udev event string should be formatted as follows: // "ACTION=[action]:KERNEL=[name]:SUBSYSTEM=[subsystem]" diff --git a/crash_reporter/unclean_shutdown_collector.h b/crash_reporter/unclean_shutdown_collector.h index 6324bc397..ed026bd74 100644 --- a/crash_reporter/unclean_shutdown_collector.h +++ b/crash_reporter/unclean_shutdown_collector.h @@ -16,7 +16,7 @@ class UncleanShutdownCollector : public CrashCollector { public: UncleanShutdownCollector(); - virtual ~UncleanShutdownCollector(); + ~UncleanShutdownCollector() override; // Enable collection - signal that a boot has started. bool Enable(); diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 3658e5114..9f1bb9cd9 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -31,7 +31,7 @@ class UserCollector : public CrashCollector { IsFeedbackAllowedFunction is_metrics_allowed, bool generate_diagnostics); - virtual ~UserCollector(); + ~UserCollector() override; // Enable collection. bool Enable() { return SetUpInternal(true); } From 334b1ae26b93078d77d6fc225bfa37514fb6e5b0 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 14 Aug 2014 07:41:22 -0400 Subject: [PATCH 189/225] crash-reporter: crash_sender: strip dashes from clientids Since Chrome has started preserving the dashes, we have to strip them ourselves when uploading reports. BUG=chromium:398217 BUG=391338 TEST=`cbuildbot x86-generic-full` passes Change-Id: I795fb7fd6feae3c4f97829a849f0271c61e84490 Reviewed-on: https://chromium-review.googlesource.com/212229 Tested-by: Mike Frysinger Reviewed-by: Ben Chan Commit-Queue: Mike Frysinger --- crash_reporter/crash_sender | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 8c15ad7c1..f0848286f 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -274,6 +274,7 @@ send_crash() { local product="$(get_key_value "${meta_path}" "upload_var_prod")" local version="$(get_key_value "${meta_path}" "upload_var_ver")" local upload_prefix="$(get_key_value "${meta_path}" "upload_prefix")" + local guid set -- \ -F "write_payload_size=${write_payload_size}" \ @@ -363,6 +364,10 @@ send_crash() { boot_mode="dev" fi + # Need to strip dashes ourselves as Chrome preserves it in the file + # nowadays. This is also what the Chrome breakpad client does. + guid=$(tr -d '-' < "${CONSENT_ID}") + local error_type="$(get_key_value "${meta_path}" "error_type")" [ "${error_type}" = "undefined" ] && error_type= @@ -421,7 +426,7 @@ send_crash() { ${image_type:+-F "image_type=${image_type}"} \ ${boot_mode:+-F "boot_mode=${boot_mode}"} \ ${error_type:+-F "error_type=${error_type}"} \ - -F "guid=<${CONSENT_ID}" \ + -F "guid=${guid}" \ -o "${report_id}" \ "$@" \ 2>"${curl_stderr}" From 0dfc9ceb8fed49a5bccca5f2225f0e17bc6c619a Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Thu, 14 Aug 2014 12:55:41 -0700 Subject: [PATCH 190/225] platform2: Replace 'OVERRIDE' with 'override' and fix linter Replace OVERRIDE macro with native C++ 'override' keyword. Also fixed the style issue of using both 'override' and 'virtual' on member function overrides. Only one is required and cpplint complains if both are specified now. BUG=None TEST=Build all affected target and ran unit tests which passed. Change-Id: I04474aacad2ca8d6c6042c84a5378961d5bd8bf2 Reviewed-on: https://chromium-review.googlesource.com/212180 Reviewed-by: Alex Vakulenko Commit-Queue: Alex Vakulenko Tested-by: Alex Vakulenko --- crash_reporter/chrome_collector_test.cc | 2 +- crash_reporter/kernel_collector_test.cc | 2 +- crash_reporter/udev_collector_test.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc index 843ca8e67..05e8f2dad 100644 --- a/crash_reporter/chrome_collector_test.cc +++ b/crash_reporter/chrome_collector_test.cc @@ -51,7 +51,7 @@ class ChromeCollectorTest : public ::testing::Test { ChromeCollector collector_; private: - void SetUp() OVERRIDE { + void SetUp() override { collector_.Initialize(CountCrash, IsMetrics); chromeos::ClearLog(); } diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index ec7ad8e11..d6414a636 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -46,7 +46,7 @@ class KernelCollectorTest : public ::testing::Test { KernelCollector collector_; private: - void SetUp() OVERRIDE { + void SetUp() override { s_crashes = 0; s_metrics = true; collector_.Initialize(CountCrash, IsMetrics); diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc index b45fdd8e4..5797854d9 100644 --- a/crash_reporter/udev_collector_test.cc +++ b/crash_reporter/udev_collector_test.cc @@ -55,7 +55,7 @@ class UdevCollectorTest : public ::testing::Test { } private: - void SetUp() OVERRIDE { + void SetUp() override { s_consent_given = true; collector_.Initialize(CountCrash, IsMetrics); From 84c28d3b001dd1f9a9bf4d4898633f7a23c2ca14 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Sat, 16 Aug 2014 15:15:14 -0700 Subject: [PATCH 191/225] platform2: Remove legacy inherit-review-settings-ok files. BUG=None TEST=Trybot run on paladin, release, and chromiumos-sdk builders. Change-Id: I11a13e64cd1fd9e3394d1f298425d29204bb06aa Reviewed-on: https://chromium-review.googlesource.com/212840 Tested-by: Ben Chan Reviewed-by: Mike Frysinger Commit-Queue: Ben Chan --- crash_reporter/inherit-review-settings-ok | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 crash_reporter/inherit-review-settings-ok diff --git a/crash_reporter/inherit-review-settings-ok b/crash_reporter/inherit-review-settings-ok deleted file mode 100644 index e69de29bb..000000000 From ea3941f630d9753b93e7be702b345163b7717c43 Mon Sep 17 00:00:00 2001 From: Thiemo Nagel Date: Thu, 21 Aug 2014 14:41:12 +0200 Subject: [PATCH 192/225] crash-reporter: Fix overzealous crash purging in guest mode. Previously, if crash_sender was invoked during a guest session all crashes that could be accessed (i.e. that were stored outside of user directories) were deleted. This is fixed by re-ordering some of the conditions in send_crashes(). The problem had been introduced by 57f6efa4: https://chromium-review.googlesource.com/200060 BUG=chromium:393334 TEST=unit tests passed TEST=try success on amd64-generic-full Change-Id: I931665891eca8e645135f7f08c15b3086fcb7d73 Reviewed-on: https://chromium-review.googlesource.com/213470 Reviewed-by: Mike Frysinger Commit-Queue: Thiemo Nagel Tested-by: Thiemo Nagel --- crash_reporter/crash_sender | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index f0848286f..c83a12ae2 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -514,20 +514,6 @@ send_crashes() { continue fi - # Remove existing crashes in case user consent has not (yet) been given or - # has been revoked. - if ! ${METRICS_CLIENT} -c; then - lecho "Crash reporting is disabled. Removing crash." - remove_report "${meta_path}" - continue - fi - - if ! is_mock && ! is_official_image; then - lecho "Not an official OS version. Removing crash." - remove_report "${meta_path}" - continue - fi - if ! is_complete_metadata "${meta_path}"; then # This report is incomplete, so if it's old, just remove it. local old_meta=$(${FIND} "${dir}" -mindepth 1 -name \ @@ -541,6 +527,12 @@ send_crashes() { continue fi + if ! is_mock && ! is_official_image; then + lecho "Not an official OS version. Removing crash." + remove_report "${meta_path}" + continue + fi + # Don't send crash reports from previous sessions while we're in guest mode # to avoid the impression that crash reporting was enabled, which it isn't. # (Don't exit right now because subsequent reports may be candidates for @@ -550,6 +542,15 @@ send_crashes() { continue fi + # Remove existing crashes in case user consent has not (yet) been given or + # has been revoked. This must come after the guest mode check because + # ${METRICS_CLIENT} always returns "not consented" in guest mode. + if ! ${METRICS_CLIENT} -c; then + lecho "Crash reporting is disabled. Removing crash." + remove_report "${meta_path}" + continue + fi + # Skip report if the upload rate is exceeded. (Don't exit right now because # subsequent reports may be candidates for deletion.) if ! check_rate; then From f3811f50c8783b24607c776844a3cf66c15c439f Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Tue, 26 Aug 2014 06:46:38 -0700 Subject: [PATCH 193/225] crash-reporter: Replace scoped_ptr with vector. BUG=None TEST=`FEATURES=test emerge-$BOARD platform2` Change-Id: I78d7766b49151f40f42ef00b8204fd2569b45991 Reviewed-on: https://chromium-review.googlesource.com/214780 Reviewed-by: Alex Vakulenko Tested-by: Ben Chan Commit-Queue: Ben Chan --- crash_reporter/crash_collector.cc | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 966970b3e..b5a7072b2 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -8,6 +8,7 @@ #include // For file creation modes. #define __STDC_FORMAT_MACROS // PRId64 #include +#include // PATH_MAX #include // For struct passwd. #include // for mode_t. #include // For waitpid. @@ -320,31 +321,31 @@ FilePath CrashCollector::GetProcessPath(pid_t pid) { bool CrashCollector::GetSymlinkTarget(const FilePath &symlink, FilePath *target) { - int max_size = 32; - scoped_ptr buffer; + ssize_t max_size = 64; + std::vector buffer; + while (true) { - buffer.reset(new char[max_size + 1]); - ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size); + buffer.resize(max_size + 1); + ssize_t size = readlink(symlink.value().c_str(), buffer.data(), max_size); if (size < 0) { int saved_errno = errno; LOG(ERROR) << "Readlink failed on " << symlink.value() << " with " << saved_errno; return false; } + buffer[size] = 0; if (size == max_size) { - // Avoid overflow when doubling. - if (max_size * 2 > max_size) { - max_size *= 2; - continue; - } else { + max_size *= 2; + if (max_size > PATH_MAX) { return false; } + continue; } break; } - *target = FilePath(buffer.get()); + *target = FilePath(buffer.data()); return true; } From f290e9873ef4cd9821ba0b03f45894e942c1115c Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Tue, 2 Sep 2014 16:26:20 -0700 Subject: [PATCH 194/225] crash-reporter: Include powerd logs with crashes. Attach relevant power manager logs to crash reports for powerd, powerd_setuid_helper, and power_supply_info. BUG=chromium:405727 TEST=induced crashes in powerd, powerd_setuid_helper, and power_supply_info, then forced crash reporting with "FORCE_OFFICIAL=1 crash_sender" and checked that the following crash reports had the expected data attached in the "Files" section: 2f710954e30472b1 (powerd) e7d985b7dbd9d158 (powerd_setuid_helper) 2213b0413fc9e408 (power_supply_info) Change-Id: Icddb7d0b99eb19b95c656f8c06d8766733eaae28 Reviewed-on: https://chromium-review.googlesource.com/216020 Tested-by: Daniel Erat Reviewed-by: Mike Frysinger Commit-Queue: Daniel Erat --- crash_reporter/crash_reporter_logs.conf | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 9c1a531ec..e4ad207dc 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -41,6 +41,19 @@ crash_reporter-udev-collection-change--i2c-atmel_mxt_ts:/usr/sbin/kernel_log_col crash_reporter-udev-collection---TouchNoise:cat /var/log/touch_noise.log # Periodically collect touch event log for debugging (crosbug.com/p/17244) crash_reporter-udev-collection---TouchEvent:cat /var/log/touch_event.log + +# Dump the last 50 lines of the last two powerd log files -- if the job has +# already restarted, we want to see the end of the previous instance's logs. +powerd:for f in $(ls -1tr /var/log/power_manager/powerd.[0-9]* | tail -2); do echo "===$(basename $f) (tail)==="; tail -50 $f; echo EOF; done +# If power_supply_info aborts (due to e.g. a bad battery), its failure message +# could end up in various places depending on which process was running it. +# Attach the end of powerd's log since it might've also logged the underlying +# problem. +power_supply_info:echo "===powerd.LATEST (tail)==="; tail -50 /var/log/power_manager/powerd.LATEST; echo EOF +# powerd_setuid_helper gets run by powerd, so its stdout/stderr will be mixed in +# with powerd's stdout/stderr. +powerd_setuid_helper:echo "===powerd.OUT (tail)==="; tail -50 /var/log/powerd.out; echo EOF + # The following rules are only for testing purposes. crash_log_test:echo hello world crash_log_recursion_test:sleep 1 && /usr/local/autotest/tests/crash_log_recursion_test From 895fa5d99f4187d67eee807cf7812d1af9853b49 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Tue, 2 Sep 2014 20:58:20 -0700 Subject: [PATCH 195/225] crash-reporter: Include base/macros.h instead of base/basictypes.h crash-reporter no longer uses the integer types from base/basictypes.h. It should simply include base/macros.h for the DISALLOW_COPY_AND_ASSIGN macro instead. This CL also marks several classes DISALLOW_COPY_AND_ASSIGN. BUG=None TEST=`FEATURES=test emerge-$BOARD crash-reporter` Change-Id: I02c90916fdda63c4d4134ed87b13382ed2fbab26 Reviewed-on: https://chromium-review.googlesource.com/216009 Reviewed-by: Alex Vakulenko Commit-Queue: Ben Chan Tested-by: Ben Chan --- crash_reporter/chrome_collector.h | 3 +++ crash_reporter/crash_collector.h | 4 ++++ crash_reporter/kernel_collector.h | 1 + crash_reporter/kernel_warning_collector.h | 3 +++ crash_reporter/udev_collector.cc | 1 - crash_reporter/udev_collector.h | 3 +++ crash_reporter/unclean_shutdown_collector.h | 3 +++ crash_reporter/user_collector.h | 3 +++ 8 files changed, 20 insertions(+), 1 deletion(-) diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h index 90e5e3879..d8602b93b 100644 --- a/crash_reporter/chrome_collector.h +++ b/crash_reporter/chrome_collector.h @@ -8,6 +8,7 @@ #include #include +#include #include // for FRIEND_TEST #include "crash-reporter/crash_collector.h" @@ -48,6 +49,8 @@ class ChromeCollector : public CrashCollector { const std::string &basename); FILE *output_file_ptr_; + + DISALLOW_COPY_AND_ASSIGN(ChromeCollector); }; #endif // CRASH_REPORTER_CHROME_COLLECTOR_H_ diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 49aba120c..c98d4a5ee 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -13,6 +13,7 @@ #include #include +#include #include // for FRIEND_TEST // User crash collector. @@ -170,6 +171,9 @@ class CrashCollector { base::FilePath forced_crash_directory_; std::string lsb_release_; base::FilePath log_config_path_; + + private: + DISALLOW_COPY_AND_ASSIGN(CrashCollector); }; #endif // CRASH_REPORTER_CRASH_COLLECTOR_H_ diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index 860256430..3899fb02c 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -10,6 +10,7 @@ #include #include +#include #include // for FRIEND_TEST #include "crash-reporter/crash_collector.h" diff --git a/crash_reporter/kernel_warning_collector.h b/crash_reporter/kernel_warning_collector.h index 0b2002735..f326b23ef 100644 --- a/crash_reporter/kernel_warning_collector.h +++ b/crash_reporter/kernel_warning_collector.h @@ -7,6 +7,7 @@ #include +#include #include // for FRIEND_TEST #include "crash-reporter/crash_collector.h" @@ -27,6 +28,8 @@ class KernelWarningCollector : public CrashCollector { // Reads the full content of the kernel warn dump and its signature. bool LoadKernelWarning(std::string *content, std::string *signature); + + DISALLOW_COPY_AND_ASSIGN(KernelWarningCollector); }; #endif // CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_ diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc index 506552772..335fa2fdc 100644 --- a/crash_reporter/udev_collector.cc +++ b/crash_reporter/udev_collector.cc @@ -8,7 +8,6 @@ #include #include -#include #include #include #include diff --git a/crash_reporter/udev_collector.h b/crash_reporter/udev_collector.h index 2b9bd34b9..68eb85a4f 100644 --- a/crash_reporter/udev_collector.h +++ b/crash_reporter/udev_collector.h @@ -8,6 +8,7 @@ #include #include +#include #include // for FRIEND_TEST #include "crash-reporter/crash_collector.h" @@ -32,6 +33,8 @@ class UdevCollector : public CrashCollector { void set_log_config_path(const std::string& path) { log_config_path_ = base::FilePath(path); } + + DISALLOW_COPY_AND_ASSIGN(UdevCollector); }; #endif // CRASH_REPORTER_UDEV_COLLECTOR_H_ diff --git a/crash_reporter/unclean_shutdown_collector.h b/crash_reporter/unclean_shutdown_collector.h index ed026bd74..d30a0b2ef 100644 --- a/crash_reporter/unclean_shutdown_collector.h +++ b/crash_reporter/unclean_shutdown_collector.h @@ -8,6 +8,7 @@ #include #include +#include #include // for FRIEND_TEST #include "crash-reporter/crash_collector.h" @@ -42,6 +43,8 @@ class UncleanShutdownCollector : public CrashCollector { const char *unclean_shutdown_file_; base::FilePath powerd_trace_path_; base::FilePath powerd_suspended_file_; + + DISALLOW_COPY_AND_ASSIGN(UncleanShutdownCollector); }; #endif // CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_ diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index 9f1bb9cd9..fe2c00b74 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -9,6 +9,7 @@ #include #include +#include #include // for FRIEND_TEST #include "crash-reporter/crash_collector.h" @@ -177,6 +178,8 @@ class UserCollector : public CrashCollector { static const char *kUserId; static const char *kGroupId; + + DISALLOW_COPY_AND_ASSIGN(UserCollector); }; #endif // CRASH_REPORTER_USER_COLLECTOR_H_ From b63e528a566ea39219bb4cafaab66ba683a65fe8 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Fri, 5 Sep 2014 08:20:59 -0700 Subject: [PATCH 196/225] Update to build against libchrome-293518. libchrome is updated from revision 271506 to 293518. This CL updates platform2 code to build against libchrome-293518 with the following changes: - LOG_ERROR_REPORT is removed (https://codereview.chromium.org/331143007) - StringToLowerASCII is moved to base namespace (https://codereview.chromium.org/448853002) BUG=chromium:411001 CQ-DEPEND=CL:216584 CQ-DEPEND=CL:216585 CQ-DEPEND=CL:216586 CQ-DEPEND=CL:216511 CQ-DEPEND=CL:217084 CQ-DEPEND=CL:217085 TEST=Trybot run on paladin, release, and chromiumos-sdk builders. Change-Id: I9fbdad30b3a7c79c1ec4e208664b8befea31a3ec Reviewed-on: https://chromium-review.googlesource.com/216589 Reviewed-by: Alex Vakulenko Tested-by: Ben Chan Commit-Queue: Ben Chan --- crash_reporter/list_proxies.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc index 17793af3e..c4a2d8c9f 100644 --- a/crash_reporter/list_proxies.cc +++ b/crash_reporter/list_proxies.cc @@ -76,7 +76,7 @@ std::deque ParseProxyString(const std::string &input) { } std::string scheme = std::string(token.begin(), space); - StringToLowerASCII(&scheme); + base::StringToLowerASCII(&scheme); // Chrome uses "socks" to mean socks4 and "proxy" to mean http. if (scheme == "socks") scheme += "4"; From ab6cc90503ca2db976a3cb9c9382a9da85c4b5a2 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Fri, 5 Sep 2014 08:21:06 -0700 Subject: [PATCH 197/225] Update code to include base/files/file_util.h file_util.h was moved from base to base/files (https://codereview.chromium.org/468253002). This CL updates platform2 code to include base/files/file_util.h instead of base/file_util.h. BUG=chromium:411001 TEST=Trybot run on paladin, release, and chromiumos-sdk builders. Change-Id: I488925b54615e131e508a460dc1a27f88168f936 Reviewed-on: https://chromium-review.googlesource.com/216851 Reviewed-by: Alex Vakulenko Tested-by: Ben Chan Commit-Queue: Ben Chan --- crash_reporter/chrome_collector.cc | 2 +- crash_reporter/chrome_collector_test.cc | 2 +- crash_reporter/crash_collector.cc | 2 +- crash_reporter/crash_collector_test.cc | 2 +- crash_reporter/crash_reporter.cc | 2 +- crash_reporter/kernel_collector.cc | 2 +- crash_reporter/kernel_collector_test.cc | 2 +- crash_reporter/kernel_warning_collector.cc | 2 +- crash_reporter/list_proxies.cc | 2 +- crash_reporter/udev_collector.cc | 2 +- crash_reporter/udev_collector_test.cc | 2 +- crash_reporter/unclean_shutdown_collector.cc | 2 +- crash_reporter/unclean_shutdown_collector_test.cc | 2 +- crash_reporter/user_collector.cc | 2 +- crash_reporter/user_collector_test.cc | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index c94ef6415..438ecbc80 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include #include diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc index 05e8f2dad..0f3b4fb78 100644 --- a/crash_reporter/chrome_collector_test.cc +++ b/crash_reporter/chrome_collector_test.cc @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index b5a7072b2..fcb08450b 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 1ffb8e423..7e4c7c12d 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 2a74f1318..a483c847b 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 08ad85b83..ab7e250fe 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -6,7 +6,7 @@ #include -#include +#include #include #include #include diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index d6414a636..654ed9de9 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -6,7 +6,7 @@ #include -#include +#include #include #include #include diff --git a/crash_reporter/kernel_warning_collector.cc b/crash_reporter/kernel_warning_collector.cc index 7082569de..b4ff3ea3c 100644 --- a/crash_reporter/kernel_warning_collector.cc +++ b/crash_reporter/kernel_warning_collector.cc @@ -4,7 +4,7 @@ #include "crash-reporter/kernel_warning_collector.h" -#include +#include #include #include #include diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc index c4a2d8c9f..282c6ae02 100644 --- a/crash_reporter/list_proxies.cc +++ b/crash_reporter/list_proxies.cc @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc index 335fa2fdc..6847d2a11 100644 --- a/crash_reporter/udev_collector.cc +++ b/crash_reporter/udev_collector.cc @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include #include diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc index 5797854d9..148cf880a 100644 --- a/crash_reporter/udev_collector_test.cc +++ b/crash_reporter/udev_collector_test.cc @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include #include +#include #include #include #include diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc index 3c8af6263..e8273b554 100644 --- a/crash_reporter/unclean_shutdown_collector.cc +++ b/crash_reporter/unclean_shutdown_collector.cc @@ -4,7 +4,7 @@ #include "crash-reporter/unclean_shutdown_collector.h" -#include +#include #include static const char kUncleanShutdownFile[] = diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc index d44ec0d96..f23174c10 100644 --- a/crash_reporter/unclean_shutdown_collector_test.cc +++ b/crash_reporter/unclean_shutdown_collector_test.cc @@ -6,7 +6,7 @@ #include -#include +#include #include #include #include diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index f387bd0ff..a3c6f9c3c 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include #include diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 242c117fe..5250c717b 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include #include From 8e774579a07b8520919205889a98dce4aecd5bc4 Mon Sep 17 00:00:00 2001 From: Benjamin Lerman Date: Wed, 9 Jul 2014 15:09:00 +0200 Subject: [PATCH 198/225] crash-reporter: Replace the crash_sender script by a service daemon. BUG=chromium:391887 TEST=Integration tests CQ-DEPEND=I02ce7593fcfae4ba1d7d3ebdf3912901e635f1c9 CQ-DEPEND=I00315ad47657cebd9e8a4a0121ecb54114a7e200 Change-Id: I2f4546c82fb3769b5f3da5d22949551412096b10 Reviewed-on: https://chromium-review.googlesource.com/208671 Tested-by: Benjamin Lerman Reviewed-by: Ben Chan --- crash_reporter/crash-reporter.gyp | 40 +- crash_reporter/crash_sender | 685 -------------------- crash_reporter/crash_sender.conf | 28 + crash_reporter/crash_sender_daemon.cc | 101 +++ crash_reporter/crash_sender_daemon.h | 43 ++ crash_reporter/crash_sender_service.cc | 853 +++++++++++++++++++++++++ crash_reporter/crash_sender_service.h | 114 ++++ crash_reporter/init/crash-sender.conf | 11 +- crash_reporter/libproxies.cc | 57 ++ crash_reporter/libproxies.h | 28 + crash_reporter/list_proxies.cc | 257 -------- crash_reporter/proxy_resolver.cc | 41 ++ crash_reporter/proxy_resolver.h | 45 ++ 13 files changed, 1353 insertions(+), 950 deletions(-) delete mode 100755 crash_reporter/crash_sender create mode 100644 crash_reporter/crash_sender.conf create mode 100644 crash_reporter/crash_sender_daemon.cc create mode 100644 crash_reporter/crash_sender_daemon.h create mode 100644 crash_reporter/crash_sender_service.cc create mode 100644 crash_reporter/crash_sender_service.h create mode 100644 crash_reporter/libproxies.cc create mode 100644 crash_reporter/libproxies.h delete mode 100644 crash_reporter/list_proxies.cc create mode 100644 crash_reporter/proxy_resolver.cc create mode 100644 crash_reporter/proxy_resolver.h diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index 79faf97e5..f36d1d0f1 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -63,17 +63,51 @@ ], }, { - 'target_name': 'list_proxies', + 'target_name': 'libproxies', + 'type': 'static_library', + 'variables': { + 'exported_deps': [ + 'libchrome-<(libbase_ver)', + ], + 'deps': ['<@(exported_deps)'], + }, + 'all_dependent_settings': { + 'variables': { + 'deps': [ + '<@(exported_deps)', + ], + }, + }, + 'sources': [ + 'libproxies.cc', + 'libproxies.h', + ], + }, + { + 'target_name': 'crash_sender', 'type': 'executable', 'variables': { 'deps': [ 'dbus-1', - 'dbus-glib-1', 'libchrome-<(libbase_ver)', + 'libchromeos-<(libbase_ver)', + 'libcurl', + 'libmetrics-<(libbase_ver)', ], }, + 'dependencies': [ + 'libproxies', + ], + 'libraries': [ + '-lvboot_host', + ], 'sources': [ - 'list_proxies.cc', + 'crash_sender_daemon.cc', + 'crash_sender_daemon.h', + 'crash_sender_service.cc', + 'crash_sender_service.h', + 'proxy_resolver.cc', + 'proxy_resolver.h', ], }, { diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender deleted file mode 100755 index c83a12ae2..000000000 --- a/crash_reporter/crash_sender +++ /dev/null @@ -1,685 +0,0 @@ -#!/bin/sh - -# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -set -e - -# Default product ID in crash report (used if GOOGLE_CRASH_* is undefined). -CHROMEOS_PRODUCT=ChromeOS - -# File whose existence implies crash reports may be sent, and whose -# contents includes our machine's anonymized guid. -CONSENT_ID="/home/chronos/Consent To Send Stats" - -# Crash sender lock in case the sender is already running. -CRASH_SENDER_LOCK="/var/lock/crash_sender" - -# Path to file that indicates a crash test is currently running. -CRASH_TEST_IN_PROGRESS_FILE="/tmp/crash-test-in-progress" - -# Path to find which is required for computing the crash rate. -FIND="/usr/bin/find" - -# Set this to 1 in the environment to allow uploading crash reports -# for unofficial versions. -FORCE_OFFICIAL=${FORCE_OFFICIAL:-0} - -# Path to hardware class description. -HWCLASS_PATH="/sys/devices/platform/chromeos_acpi/HWID" - -# Path to file that indicates this is a developer image. -LEAVE_CORE_FILE="/root/.leave_core" - -# Path to list_proxies. -LIST_PROXIES="/usr/bin/list_proxies" - -# Maximum crashes to send per day. -MAX_CRASH_RATE=${MAX_CRASH_RATE:-32} - -# Path to metrics_client. -METRICS_CLIENT="/usr/bin/metrics_client" - -# File whose existence mocks crash sending. If empty we pretend the -# crash sending was successful, otherwise unsuccessful. -MOCK_CRASH_SENDING="/tmp/mock-crash-sending" - -# Set this to 1 in the environment to pretend to have booted in developer -# mode. This is used by autotests. -MOCK_DEVELOPER_MODE=${MOCK_DEVELOPER_MODE:-0} - -# Ignore PAUSE_CRASH_SENDING file if set. -OVERRIDE_PAUSE_SENDING=${OVERRIDE_PAUSE_SENDING:-0} - -# File whose existence causes crash sending to be delayed (for testing). -# Must be stateful to enable testing kernel crashes. -PAUSE_CRASH_SENDING="/var/lib/crash_sender_paused" - -# URL to send official build crash reports to. -REPORT_UPLOAD_PROD_URL="https://clients2.google.com/cr/report" - -# Path to a directory of restricted certificates which includes -# a certificate for ${REPORT_UPLOAD_PROD_URL}. -RESTRICTED_CERTIFICATES_PATH="/usr/share/chromeos-ca-certificates" - -# File whose existence implies we're running and not to start again. -RUN_FILE="/var/run/crash_sender.pid" - -# Maximum time to sleep between sends. -SECONDS_SEND_SPREAD=${SECONDS_SEND_SPREAD:-600} - -# The syslog tag for all logging we emit. -TAG="$(basename $0)[$$]" - -# Directory to store timestamp files indicating the uploads in the past 24 -# hours. -TIMESTAMPS_DIR="/var/lib/crash_sender" - -# Temp directory for this process. -TMP_DIR="" - -# Chrome's crash report log file. -CHROME_CRASH_LOG="/var/log/chrome/Crash Reports/uploads.log" - -lecho() { - logger -t "${TAG}" "$@" -} - -# Returns true if mock is enabled. -is_mock() { - [ -f "${MOCK_CRASH_SENDING}" ] && return 0 - return 1 -} - -is_mock_successful() { - local mock_in=$(cat "${MOCK_CRASH_SENDING}") - [ "${mock_in}" = "" ] && return 0 # empty file means success - return 1 -} - -cleanup() { - if [ -n "${TMP_DIR}" ]; then - rm -rf "${TMP_DIR}" - fi - rm -f "${RUN_FILE}" - crash_done -} - -crash_done() { - if is_mock; then - # For testing purposes, emit a message to log so that we - # know when the test has received all the messages from this run. - lecho "crash_sender done." - fi -} - -is_official_image() { - [ ${FORCE_OFFICIAL} -ne 0 ] && return 0 - grep ^CHROMEOS_RELEASE_DESCRIPTION /etc/lsb-release | grep -q Official -} - -# Returns 0 if the a crash test is currently running. NOTE: Mirrors -# crash_collector.cc:CrashCollector::IsCrashTestInProgress(). -is_crash_test_in_progress() { - [ -f "${CRASH_TEST_IN_PROGRESS_FILE}" ] && return 0 - return 1 -} - -# Returns 0 if we should consider ourselves to be running on a developer -# image. NOTE: Mirrors crash_collector.cc:CrashCollector::IsDeveloperImage(). -is_developer_image() { - # If we're testing crash reporter itself, we don't want to special-case - # for developer images. - is_crash_test_in_progress && return 1 - [ -f "${LEAVE_CORE_FILE}" ] && return 0 - return 1 -} - -# Returns 0 if we should consider ourselves to be running on a test image. -is_test_image() { - # If we're testing crash reporter itself, we don't want to special-case - # for test images. - is_crash_test_in_progress && return 1 - case $(get_channel) in - test*) return 0;; - esac - return 1 -} - -# Returns 0 if the machine booted up in developer mode. -is_developer_mode() { - [ ${MOCK_DEVELOPER_MODE} -ne 0 ] && return 0 - # If we're testing crash reporter itself, we don't want to special-case - # for developer mode. - is_crash_test_in_progress && return 1 - crossystem "devsw_boot?1" # exit status will be accurate -} - -# Generate a uniform random number in 0..max-1. -generate_uniform_random() { - local max=$1 - local random="$(od -An -N4 -tu /dev/urandom)" - echo $((random % max)) -} - -# Check if sending a crash now does not exceed the maximum 24hr rate and -# commit to doing so, if not. -check_rate() { - mkdir -p ${TIMESTAMPS_DIR} - # Only consider minidumps written in the past 24 hours by removing all older. - ${FIND} "${TIMESTAMPS_DIR}" -mindepth 1 -mmin +$((24 * 60)) \ - -exec rm -- '{}' ';' - local sends_in_24hrs=$(echo "${TIMESTAMPS_DIR}"/* | wc -w) - lecho "Current send rate: ${sends_in_24hrs}sends/24hrs" - if [ ${sends_in_24hrs} -ge ${MAX_CRASH_RATE} ]; then - lecho "Cannot send more crashes:" - lecho " current ${sends_in_24hrs}send/24hrs >= " \ - "max ${MAX_CRASH_RATE}send/24hrs" - return 1 - fi - mktemp "${TIMESTAMPS_DIR}"/XXXX > /dev/null - return 0 -} - -# Gets the base part of a crash report file, such as name.01234.5678.9012 from -# name.01234.5678.9012.meta or name.01234.5678.9012.log.tar.xz. We make sure -# "name" is sanitized in CrashCollector::Sanitize to not include any periods. -get_base() { - echo "$1" | cut -d. -f-4 -} - -get_extension() { - local extension="${1##*.}" - local filename="${1%.*}" - # For gzipped file, we ignore .gz and get the real extension - if [ "${extension}" = "gz" ]; then - echo "${filename##*.}" - else - echo "${extension}" - fi -} - -# Return which kind of report the given metadata file relates to -get_kind() { - local payload="$(get_key_value "$1" "payload")" - if [ ! -r "${payload}" ]; then - lecho "Missing payload: ${payload}" - echo "undefined" - return - fi - local kind="$(get_extension "${payload}")" - if [ "${kind}" = "dmp" ]; then - echo "minidump" - return - fi - echo "${kind}" -} - -get_key_value() { - local file="$1" key="$2" value - - if [ -f "${file}" ]; then - # Return the first entry. There shouldn't be more than one anyways. - # Substr at length($1) + 2 skips past the key and following = sign (awk - # uses 1-based indexes), but preserves embedded = characters. - value=$(sed -n "/^${key}[[:space:]]*=/{s:^[^=]*=::p;q}" "${file}") - fi - - echo "${value:-undefined}" -} - -get_keys() { - local file="$1" regex="$2" - - awk -F'[[:space:]=]' -vregex="${regex}" \ - 'match($1, regex) { print $1 }' "${file}" -} - -# Return the board name. -get_board() { - get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_BOARD" -} - -# Return the channel name (sans "-channel" suffix). -get_channel() { - get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_TRACK" | - sed 's:-channel$::' -} - -# Return the hardware class or "undefined". -get_hardware_class() { - if [ -r "${HWCLASS_PATH}" ]; then - cat "${HWCLASS_PATH}" - elif crossystem hwid > /dev/null 2>&1; then - echo "$(crossystem hwid)" - else - echo "undefined" - fi -} - -send_crash() { - local meta_path="$1" - local report_payload="$(get_key_value "${meta_path}" "payload")" - local kind="$(get_kind "${meta_path}")" - local exec_name="$(get_key_value "${meta_path}" "exec_name")" - local url="${REPORT_UPLOAD_PROD_URL}" - local chromeos_version="$(get_key_value "${meta_path}" "ver")" - local board="$(get_board)" - local hwclass="$(get_hardware_class)" - local write_payload_size="$(get_key_value "${meta_path}" "payload_size")" - local log="$(get_key_value "${meta_path}" "log")" - local sig="$(get_key_value "${meta_path}" "sig")" - local send_payload_size="$(stat --printf=%s "${report_payload}" 2>/dev/null)" - local product="$(get_key_value "${meta_path}" "upload_var_prod")" - local version="$(get_key_value "${meta_path}" "upload_var_ver")" - local upload_prefix="$(get_key_value "${meta_path}" "upload_prefix")" - local guid - - set -- \ - -F "write_payload_size=${write_payload_size}" \ - -F "send_payload_size=${send_payload_size}" - if [ "${sig}" != "undefined" ]; then - set -- "$@" \ - -F "sig=${sig}" \ - -F "sig2=${sig}" - fi - if [ -r "${report_payload}" ]; then - set -- "$@" \ - -F "upload_file_${kind}=@${report_payload}" - fi - if [ "${log}" != "undefined" -a -r "${log}" ]; then - set -- "$@" \ - -F "log=@${log}" - fi - - if [ "${upload_prefix}" = "undefined" ]; then - upload_prefix="" - fi - - # Grab any variable that begins with upload_. - local v - for k in $(get_keys "${meta_path}" "^upload_"); do - v="$(get_key_value "${meta_path}" "${k}")" - case ${k} in - # Product & version are handled separately. - upload_var_prod) ;; - upload_var_ver) ;; - upload_var_*) - set -- "$@" -F "${upload_prefix}${k#upload_var_}=${v}" - ;; - upload_file_*) - if [ -r "${v}" ]; then - set -- "$@" -F "${upload_prefix}${k#upload_file_}=@${v}" - fi - ;; - esac - done - - # When uploading Chrome reports we need to report the right product and - # version. If the meta file does not specify it, use GOOGLE_CRASH_ID - # as the product and GOOGLE_CRASH_VERSION_ID as the version. - if [ "${product}" = "undefined" ]; then - product="$(get_key_value /etc/os-release 'GOOGLE_CRASH_ID')" - fi - if [ "${version}" = "undefined" ]; then - version="$(get_key_value /etc/os-release 'GOOGLE_CRASH_VERSION_ID')" - fi - - # If GOOGLE_CRASH_* is undefined, we look for ID and VERSION_ID in - # /etc/os-release. - if [ "${product}" = "undefined" ]; then - product="$(get_key_value /etc/os-release 'ID')" - fi - if [ "${version}" = "undefined" ]; then - version="$(get_key_value /etc/os-release 'VERSION_ID')" - fi - - # If ID or VERSION_ID is undefined, we use the default product name - # and CHROMEOS_RELEASE_VERSION from /etc/lsb-release. - if [ "${product}" = "undefined" ]; then - product="${CHROMEOS_PRODUCT}" - fi - if [ "${version}" = "undefined" ]; then - version="${chromeos_version}" - fi - - local image_type - if is_test_image; then - image_type="test" - elif is_developer_image; then - image_type="dev" - elif [ ${FORCE_OFFICIAL} -ne 0 ]; then - image_type="force-official" - elif is_mock && ! is_mock_successful; then - image_type="mock-fail" - fi - - local boot_mode - if ! crossystem "cros_debug" > /dev/null 2>&1; then - # Sanity-check failed that makes sure crossystem exists. - lecho "Cannot determine boot mode due to error running crossystem command" - boot_mode="missing-crossystem" - elif is_developer_mode; then - boot_mode="dev" - fi - - # Need to strip dashes ourselves as Chrome preserves it in the file - # nowadays. This is also what the Chrome breakpad client does. - guid=$(tr -d '-' < "${CONSENT_ID}") - - local error_type="$(get_key_value "${meta_path}" "error_type")" - [ "${error_type}" = "undefined" ] && error_type= - - lecho "Sending crash:" - if [ "${product}" != "${CHROMEOS_PRODUCT}" ]; then - lecho " Sending crash report on behalf of ${product}" - fi - lecho " Metadata: ${meta_path} (${kind})" - lecho " Payload: ${report_payload}" - lecho " Version: ${version}" - [ -n "${image_type}" ] && lecho " Image type: ${image_type}" - [ -n "${boot_mode}" ] && lecho " Boot mode: ${boot_mode}" - if is_mock; then - lecho " Product: ${product}" - lecho " URL: ${url}" - lecho " Board: ${board}" - lecho " HWClass: ${hwclass}" - lecho " write_payload_size: ${write_payload_size}" - lecho " send_payload_size: ${send_payload_size}" - if [ "${log}" != "undefined" ]; then - lecho " log: @${log}" - fi - if [ "${sig}" != "undefined" ]; then - lecho " sig: ${sig}" - fi - fi - lecho " Exec name: ${exec_name}" - [ -n "${error_type}" ] && lecho " Error type: ${error_type}" - if is_mock; then - if ! is_mock_successful; then - lecho "Mocking unsuccessful send" - return 1 - fi - lecho "Mocking successful send" - return 0 - fi - - # Read in the first proxy, if any, for a given URL. NOTE: The - # double-quotes are necessary due to a bug in dash with the "local" - # builtin command and values that have spaces in them (see - # "https://bugs.launchpad.net/ubuntu/+source/dash/+bug/139097"). - local proxy="`${LIST_PROXIES} -quiet "${url}" | head -1`" - # if a direct connection should be used, unset the proxy variable. - [ "${proxy}" = "direct://" ] && proxy= - local report_id="${TMP_DIR}/report_id" - local curl_stderr="${TMP_DIR}/curl_stderr" - - set +e - curl "${url}" -v ${proxy:+--proxy "$proxy"} \ - --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \ - -F "prod=${product}" \ - -F "ver=${version}" \ - -F "board=${board}" \ - -F "hwclass=${hwclass}" \ - -F "exec_name=${exec_name}" \ - ${image_type:+-F "image_type=${image_type}"} \ - ${boot_mode:+-F "boot_mode=${boot_mode}"} \ - ${error_type:+-F "error_type=${error_type}"} \ - -F "guid=${guid}" \ - -o "${report_id}" \ - "$@" \ - 2>"${curl_stderr}" - curl_result=$? - set -e - - if [ ${curl_result} -eq 0 ]; then - local id="$(cat "${report_id}")" - local product_name - local timestamp="$(date +%s)" - case ${product} in - Chrome_ChromeOS) - if is_official_image; then - product_name="Chrome" - else - product_name="Chromium" - fi - ;; - *) - if is_official_image; then - product_name="ChromeOS" - else - product_name="ChromiumOS" - fi - ;; - esac - printf '%s,%s,%s\n' \ - "${timestamp}" "${id}" "${product_name}" >> "${CHROME_CRASH_LOG}" - lecho "Crash report receipt ID ${id}" - else - lecho "Crash sending failed with exit code ${curl_result}: " \ - "$(cat "${curl_stderr}")" - fi - - rm -f "${report_id}" - - return ${curl_result} -} - -# *.meta files always end with done=1 so we can tell if they are complete. -is_complete_metadata() { - grep -q "done=1" "$1" -} - -# Remove the given report path. -remove_report() { - local base="${1%.*}" - rm -f -- "${base}".* -} - -# Send all crashes from the given directory. This applies even when we're on a -# 3G connection (see crosbug.com/3304 for discussion). -send_crashes() { - local dir="$1" - - if [ ! -d "${dir}" ]; then - return - fi - - # Consider any old files which still have no corresponding meta file - # as orphaned, and remove them. - for old_file in $(${FIND} "${dir}" -mindepth 1 \ - -mmin +$((24 * 60)) -type f); do - if [ ! -e "$(get_base "${old_file}").meta" ]; then - lecho "Removing old orphaned file: ${old_file}." - rm -f -- "${old_file}" - fi - done - - # Look through all metadata (*.meta) files, oldest first. That way, the rate - # limit does not stall old crashes if there's a high amount of new crashes - # coming in. - # For each crash report, first evaluate conditions that might lead to its - # removal to honor user choice and to free disk space as soon as possible, - # then decide whether it should be sent right now or kept for later sending. - for meta_path in $(ls -1tr "${dir}"/*.meta 2>/dev/null); do - lecho "Considering metadata ${meta_path}." - - local kind=$(get_kind "${meta_path}") - if [ "${kind}" != "minidump" ] && \ - [ "${kind}" != "kcrash" ] && \ - [ "${kind}" != "log" ]; then - lecho "Unknown report kind ${kind}. Removing report." - remove_report "${meta_path}" - continue - fi - - if ! is_complete_metadata "${meta_path}"; then - # This report is incomplete, so if it's old, just remove it. - local old_meta=$(${FIND} "${dir}" -mindepth 1 -name \ - $(basename "${meta_path}") -mmin +$((24 * 60)) -type f) - if [ -n "${old_meta}" ]; then - lecho "Removing old incomplete metadata." - remove_report "${meta_path}" - else - lecho "Ignoring recent incomplete metadata." - fi - continue - fi - - if ! is_mock && ! is_official_image; then - lecho "Not an official OS version. Removing crash." - remove_report "${meta_path}" - continue - fi - - # Don't send crash reports from previous sessions while we're in guest mode - # to avoid the impression that crash reporting was enabled, which it isn't. - # (Don't exit right now because subsequent reports may be candidates for - # deletion.) - if ${METRICS_CLIENT} -g; then - lecho "Guest mode has been entered. Delaying crash sending until exited." - continue - fi - - # Remove existing crashes in case user consent has not (yet) been given or - # has been revoked. This must come after the guest mode check because - # ${METRICS_CLIENT} always returns "not consented" in guest mode. - if ! ${METRICS_CLIENT} -c; then - lecho "Crash reporting is disabled. Removing crash." - remove_report "${meta_path}" - continue - fi - - # Skip report if the upload rate is exceeded. (Don't exit right now because - # subsequent reports may be candidates for deletion.) - if ! check_rate; then - lecho "Sending ${meta_path} would exceed rate. Leaving for later." - continue - fi - - # The .meta file should be written *after* all to-be-uploaded files that it - # references. Nevertheless, as a safeguard, a hold-off time of thirty - # seconds after writing the .meta file is ensured. Also, sending of crash - # reports is spread out randomly by up to SECONDS_SEND_SPREAD. Thus, for - # the sleep call the greater of the two delays is used. - local now=$(date +%s) - local holdoff_time=$(($(stat --format=%Y "${meta_path}") + 30 - ${now})) - local spread_time=$(generate_uniform_random "${SECONDS_SEND_SPREAD}") - local sleep_time - if [ ${spread_time} -gt ${holdoff_time} ]; then - sleep_time="${spread_time}" - else - sleep_time="${holdoff_time}" - fi - lecho "Scheduled to send in ${sleep_time}s." - if ! is_mock; then - if ! sleep "${sleep_time}"; then - lecho "Sleep failed" - return 1 - fi - fi - - # Try to upload. - if ! send_crash "${meta_path}"; then - lecho "Problem sending ${meta_path}, not removing." - continue - fi - - # Send was successful, now remove. - lecho "Successfully sent crash ${meta_path} and removing." - remove_report "${meta_path}" - done -} - -usage() { - cat <= Set env |var| to |val| (only some vars) -EOF - exit ${1:-1} -} - -parseargs() { - # Parse the command line arguments. - while [ $# -gt 0 ]; do - case $1 in - -e) - shift - case $1 in - FORCE_OFFICIAL=*|\ - MAX_CRASH_RATE=*|\ - MOCK_DEVELOPER_MODE=*|\ - OVERRIDE_PAUSE_SENDING=*|\ - SECONDS_SEND_SPREAD=*) - export "$1" - ;; - *) - lecho "Unknown var passed to -e: $1" - exit 1 - ;; - esac - ;; - -h) - usage 0 - ;; - *) - lecho "Unknown options: $*" - exit 1 - ;; - esac - shift - done -} - -main() { - trap cleanup EXIT INT TERM - - parseargs "$@" - - if [ -e "${PAUSE_CRASH_SENDING}" ] && \ - [ ${OVERRIDE_PAUSE_SENDING} -eq 0 ]; then - lecho "Exiting early due to ${PAUSE_CRASH_SENDING}." - exit 1 - fi - - if is_test_image; then - lecho "Exiting early due to test image." - exit 1 - fi - - # We don't perform checks on this because we have a master lock with the - # CRASH_SENDER_LOCK file. This pid file is for the system to keep track - # (like with autotests) that we're still running. - echo $$ > "${RUN_FILE}" - - for dependency in "${FIND}" "${METRICS_CLIENT}" \ - "${RESTRICTED_CERTIFICATES_PATH}"; do - if [ ! -x "${dependency}" ]; then - lecho "Fatal: Crash sending disabled: ${dependency} not found." - exit 1 - fi - done - - TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXXXX)" - - # Send system-wide crashes - send_crashes "/var/spool/crash" - - # Send user-specific crashes - local d - for d in /home/chronos/crash /home/chronos/u-*/crash; do - send_crashes "${d}" - done -} - -( -if ! flock -n 9; then - lecho "Already running; quitting." - crash_done - exit 1 -fi -main "$@" -) 9>"${CRASH_SENDER_LOCK}" diff --git a/crash_reporter/crash_sender.conf b/crash_reporter/crash_sender.conf new file mode 100644 index 000000000..5611c100c --- /dev/null +++ b/crash_reporter/crash_sender.conf @@ -0,0 +1,28 @@ +# Copyright 2014 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can +# be found in the LICENSE file. + +# This file has the format: +# NAME=value + +# Product ID in crash report. +CHROMEOS_PRODUCT=ChromeOS + +# Set this to 1 to allow uploading crash reports for unofficial versions. +FORCE_OFFICIAL=0 + +# Maximum crashes to send per day. +MAX_CRASH_RATE=32 + +# Set this to 1 to pretend to have booted in developer mode. This is used by +# autotests. +MOCK_DEVELOPER_MODE=0 + +# Ignore PAUSE_CRASH_SENDING file if set. +OVERRIDE_PAUSE_SENDING=0 + +# URL to send official build crash reports to. +REPORT_UPLOAD_PROD_URL=https://clients2.google.com/cr/report + +# Maximum time to sleep between sends. +SECONDS_SEND_SPREAD=600 diff --git a/crash_reporter/crash_sender_daemon.cc b/crash_reporter/crash_sender_daemon.cc new file mode 100644 index 000000000..f477bdb59 --- /dev/null +++ b/crash_reporter/crash_sender_daemon.cc @@ -0,0 +1,101 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crash-reporter/crash_sender_daemon.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +// Parameter to specify a custom config file. +const char kSwitchCustomConfigFile[] = "config"; + +const char kDefaultConfigFile[] = "/etc/crash_sender.conf"; + +const int kTerminationSignals[] = { SIGTERM, SIGINT }; +const int kNumTerminationSignals = arraysize(kTerminationSignals); +} // namespace + + +namespace crash_reporter { +CrashSenderDaemon::CrashSenderDaemon(const base::FilePath& config_file) + : config_file_(config_file) {} + +CrashSenderDaemon::~CrashSenderDaemon() { +} + +void CrashSenderDaemon::Run() { + base::RunLoop loop; + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SYSTEM; + options.disconnected_callback = loop.QuitClosure(); + + scoped_refptr bus = new dbus::Bus(options); + CrashSenderConfiguration config = + CrashSenderService::ParseConfiguration(config_file_); + scoped_ptr impl( + new DbusCrashSenderServiceImpl(config)); + + CHECK(impl->Start(bus)) << "Failed to start crash sender service"; + crash_sender_service_ = impl.Pass(); + + for (size_t i = 0; i < kNumTerminationSignals; ++i) { + async_signal_handler_.RegisterHandler( + kTerminationSignals[i], + base::Bind(&CrashSenderDaemon::Shutdown, base::Unretained(this))); + } + async_signal_handler_.RegisterHandler( + SIGHUP, base::Bind(&CrashSenderDaemon::Restart, base::Unretained(this))); + async_signal_handler_.Init(); + + loop.Run(); + + bus->ShutdownAndBlock(); +} + +bool CrashSenderDaemon::Shutdown(const struct signalfd_siginfo& info) { + loop_.PostTask(FROM_HERE, loop_.QuitClosure()); + // Unregister the signal handler. + return true; +} + +bool CrashSenderDaemon::Restart(const struct signalfd_siginfo& info) { + CrashSenderConfiguration config = + CrashSenderService::ParseConfiguration(config_file_); + crash_sender_service_->Restart(config); + // Keep listening to the signal. + return false; +} + +} // namespace crash_reporter + +int main(int argc, char** argv) { + CommandLine::Init(argc, argv); + CommandLine* args = CommandLine::ForCurrentProcess(); + + // Some libchrome calls need this. + base::AtExitManager at_exit_manager; + + chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogToStderr); + + base::FilePath config_file = + args->GetSwitchValuePath(kSwitchCustomConfigFile); + if (config_file.empty()) { + config_file = base::FilePath(FILE_PATH_LITERAL(kDefaultConfigFile)); + } else { + LOG(INFO) << "Using crash configuration at: " << config_file.value(); + } + + crash_reporter::CrashSenderDaemon daemon(config_file); + daemon.Run(); + return 0; +} diff --git a/crash_reporter/crash_sender_daemon.h b/crash_reporter/crash_sender_daemon.h new file mode 100644 index 000000000..462a53d27 --- /dev/null +++ b/crash_reporter/crash_sender_daemon.h @@ -0,0 +1,43 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRASH_REPORTER_CRASH_SENDER_DAEMON_H_ +#define CRASH_REPORTER_CRASH_SENDER_DAEMON_H_ + +#include +#include +#include +#include + +#include "crash-reporter/crash_sender_service.h" + +namespace crash_reporter { + +class CrashSenderDaemon { + public: + // |config_file| specifies the config file for the crash sender. + explicit CrashSenderDaemon(const base::FilePath& config_file); + ~CrashSenderDaemon(); + + // Does all the work. Blocks until the daemon is finished. + void Run(); + + private: + base::MessageLoopForIO loop_; + base::FilePath config_file_; + scoped_ptr crash_sender_service_; + chromeos::AsynchronousSignalHandler async_signal_handler_; + + // Shutdown the sender. + bool Shutdown(const signalfd_siginfo& info); + + // Restart the service, reading the configuration file again. + bool Restart(const signalfd_siginfo& info); + + DISALLOW_COPY_AND_ASSIGN(CrashSenderDaemon); +}; + +} // namespace crash_reporter + +#endif // CRASH_REPORTER_CRASH_SENDER_DAEMON_H_ diff --git a/crash_reporter/crash_sender_service.cc b/crash_reporter/crash_sender_service.cc new file mode 100644 index 000000000..04fb23cab --- /dev/null +++ b/crash_reporter/crash_sender_service.cc @@ -0,0 +1,853 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crash-reporter/crash_sender_service.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vboot/crossystem.h" + +namespace { + +// Default product ID in crash report (used if GOOGLE_CRASH_* is undefined). +const char kDefaultProduct[] = "ChromeOS"; + +// File whose existence implies crash reports may be sent, and whose +// contents includes our machine's anonymized guid. +const char kConsentIdPath[] = "/home/chronos/Consent To Send Stats"; + +// Crash sender lock in case the sender is already running. +const char kCrashSenderLockPath[] = "/var/lock/crash_sender"; + +// Crash sender lock in case the sender is already running for tests. +const char kCrashSenderLockForTestsPath[] = "/var/lock/crash_sender_test"; + +// Path to file that indicates a crash test is currently running. +const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress"; + +// Path to hardware class description. +const char kHWClassPath[] = "/sys/devices/platform/chromeos_acpi/HWID"; + +// Path to file that indicates this is a developer image. +const char kLeaveCorePath[] = "/root/.leave_core"; + +// File whose existence causes crash sending to be delayed (for testing). +// Must be stateful to enable testing kernel crashes. +const char kPauseCrashSendingPath[] = "/var/lib/crash_sender_paused"; + +// Path to a directory of restricted certificates which includes +// a certificate for ${REPORT_UPLOAD_PROD_URL}. +const char kRestrictedCertificatesPath[] = + "/usr/share/chromeos-ca-certificates"; + +// File whose existence implies we're running and not to start again. +const char kRunFilePath[] = "/var/run/crash_sender.pid"; + +// Directory to store timestamp files indicating the uploads in the past 24 +// hours. +const char kTimestampsDirPath[] = "/var/lib/crash_sender"; + +// Chrome's crash report log file. +const char kChromeCrashLogPath[] = "/var/log/chrome/Crash Reports/uploads.log"; + +// File whose existence mocks crash sending. If empty we pretend the crash +// sending was successful, otherwise unsuccessful. +const char kMockCrashSendingPath[] = "/tmp/mock-crash-sending"; + +// Configuration keys. +const char kForceOfficial[] = "FORCE_OFFICIAL"; +const char kMaxCrashRate[] = "MAX_CRASH_RATE"; +const char kMockDeveloperMode[] = "MOCK_DEVELOPER_MODE"; +const char kOverridePauseSending[] = "OVERRIDE_PAUSE_SENDING"; +const char kReportUploadProdUrl[] = "REPORT_UPLOAD_PROD_URL"; +const char kSecondsSendSpread[] = "SECONDS_SEND_SPREAD"; + +// Owns a curl handle. +class ScopedCurl { + public: + explicit ScopedCurl(bool mock) : mock_(mock) { + if (!mock) + curl_ = curl_easy_init(); + } + + ~ScopedCurl() { + if (mock_) + return; + + if (post_) + curl_formfree(post_); + + curl_easy_cleanup(curl_); + } + + CURL* curl() const { return curl_; } + + void AddMultipartContent(std::string key, std::string value) { + LOG(INFO) << key << ": " << value; + if (mock_) + return; + curl_formadd(&post_, &last_, + CURLFORM_COPYNAME, key.c_str(), + CURLFORM_COPYCONTENTS, value.c_str(), + CURLFORM_END); + } + + void AddFile(std::string key, base::FilePath file) { + LOG(INFO) << key << ": " << file.value(); + if (mock_) + return; + curl_formadd(&post_, &last_, + CURLFORM_COPYNAME, key.c_str(), + CURLFORM_FILE, file.value().c_str(), + CURLFORM_END); + } + + CURLcode perform() { + CHECK(!mock_); + curl_easy_setopt(curl_, CURLOPT_HTTPPOST, post_); + return curl_easy_perform(curl_); + } + + private: + bool mock_; + CURL* curl_ = nullptr; + struct curl_httppost* post_ = nullptr; + struct curl_httppost* last_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(ScopedCurl); +}; + +// Comparison function. +bool order_meta_files(const crash_reporter::MetaFile& f1, + const crash_reporter::MetaFile& f2) { + return std::make_tuple(f1.modification_time, f1.path.value()) < + std::make_tuple(f2.modification_time, f2.path.value()); +} + +// Return the list of directories containing crashes. +std::vector GetCrashDirectories() { + std::vector result; + base::FilePath system_wide("/var/spool/crash"); + if (base::DirectoryExists(system_wide)) + result.push_back(system_wide); + + base::FilePath main_user("/home/chronos/crash"); + if (base::DirectoryExists(main_user)) + result.push_back(main_user); + + base::FileEnumerator enumCrashDirectories( + base::FilePath("/home/chronos"), false, base::FileEnumerator::DIRECTORIES, + FILE_PATH_LITERAL("u-*")); + for (base::FilePath dir = enumCrashDirectories.Next(); !dir.empty(); + dir = enumCrashDirectories.Next()) { + base::FilePath crash_dir = dir.Append("crash"); + if (base::DirectoryExists(crash_dir)) + result.push_back(crash_dir); + } + return result; +} + +// Returns all the files in a given directory with the time of last +// modification. +std::vector> GetOrderedFiles( + const base::FilePath& dir) { + std::vector> files; + base::FileEnumerator enumFiles(dir, false, base::FileEnumerator::FILES); + for (base::FilePath file = enumFiles.Next(); !file.empty(); + file = enumFiles.Next()) { + files.push_back( + std::make_pair(enumFiles.GetInfo().GetLastModifiedTime(), file)); + } + return files; +} + +// Parse a file containing key value of the form: +// KEY=VALUE +std::map ParseKeyValueFile( + const base::FilePath& file) { + std::map result; + std::string content; + if (!base::ReadFileToString(file, &content)) { + LOG(WARNING) << "Unable to read key values from: " << file.value(); + return result; + } + base::StringPairs pairs; + base::SplitStringIntoKeyValuePairs(content, '=', '\n', &pairs); + for (base::StringPairs::const_iterator pair = pairs.begin(); + pair != pairs.end(); ++pair) { + if (!pair->first.empty() && pair->first[0] != '#') + result[pair->first] = pair->second; + } + + return result; +} + +// Returns the value associated to |key| in |map|, or |default_value| if |map| +// doesn't contain |key|. +std::string GetValue(const std::map& map, + const std::string& key, const std::string& default_value) { + std::map::const_iterator it = map.find(key); + if (it != map.end()) + return it->second; + + return default_value; +} + +// As |GetValue| for values of type |base::FilePath|. +base::FilePath GetPathValue(const std::map& map, + const std::string& key, + const base::FilePath& default_value) { + std::map::const_iterator it = map.find(key); + if (it != map.end()) + return base::FilePath(it->second); + + return default_value; +} + +// As |GetValue| for values of type |int|. +int GetIntValue(const std::map& map, + const std::string& key, int default_value) { + std::map::const_iterator it = map.find(key); + if (it != map.end()) { + int result; + if (base::StringToInt(it->second, &result)) { + return result; + } + } + return default_value; +} + +// Remove the report for the given |meta_file|. +void RemoveReport(const base::FilePath& meta_file) { + LOG(INFO) << "Removing report: " << meta_file.value(); + base::FilePath directory = meta_file.DirName(); + base::FilePath template_value = meta_file.ReplaceExtension(".*").BaseName(); + + base::FileEnumerator filesToDelete( + directory, false, base::FileEnumerator::FILES, template_value.value()); + for (base::FilePath file = filesToDelete.Next(); !file.empty(); + file = filesToDelete.Next()) { + if (!base::DeleteFile(file, false)) + LOG(WARNING) << "Unable to delete " << file.value(); + } +} + +// Returns the extenstion of the given file, stripping .gz if the file is +// compressed. +std::string GetExtension(const base::FilePath& file) { + std::string extension = file.FinalExtension(); + if (extension == ".gz") + extension = file.RemoveFinalExtension().FinalExtension(); + + if (!extension.empty()) { + DCHECK_EQ(extension[0], '.'); + extension = extension.substr(1); + } + return extension; +} + +// Returns the report kind. +std::string GetKind(const crash_reporter::MetaFile& meta_file) { + base::FilePath payload = + GetPathValue(meta_file.meta_information, "payload", base::FilePath()); + if (payload.value().empty() || !base::PathExists(payload)) { + LOG(WARNING) << "Missing payload on file: " << meta_file.path.value(); + return ""; + } + std::string kind = GetExtension(payload); + if (kind == "dmp") { + return "minidump"; + } + return kind; +} + +// Callback function for curl. It delegates to a callback passed as additional +// data. +size_t CurlWriteData(void* buffer, size_t size, size_t nmemb, void* data) { + base::Callback* callback = + static_cast*>(data); + return callback->Run(buffer, size * nmemb); +} + +size_t AppendDataToString(std::string* data, const void* buffer, size_t size) { + data->append(reinterpret_cast(buffer), size); + return size; +} + + +} // namespace + +namespace crash_reporter { + +CrashSenderService::CrashSenderService(const CrashSenderConfiguration& config) + : config_(config) { + metrics_lib_.Init(); +} + +CrashSenderService::~CrashSenderService() {} + +bool CrashSenderService::Start(ProxyResolver* proxy_resolver) { + proxy_resolver_ = proxy_resolver; + std::map lsb_release_values = + ParseKeyValueFile(base::FilePath("/etc/lsb-release")); + + board_ = lsb_release_values["CHROMEOS_RELEASE_BOARD"]; + if (board_.empty()) { + LOG(ERROR) << "Unable to retrieve board information."; + return false; + } + + channel_ = lsb_release_values["CHROMEOS_RELEASE_TRACK"]; + const char kChannelSuffix[] = "-channel"; + if (EndsWith(channel_, kChannelSuffix, true)) + channel_ = + channel_.substr(0, channel_.size() - arraysize(kChannelSuffix) + 1); + + if (channel_.empty()) { + LOG(ERROR) << "Unable to retrieve channel information."; + return false; + } + + official_ = + (lsb_release_values["CHROMEOS_RELEASE_DESCRIPTION"].find("Official") != + std::string::npos); + + std::map os_release_values = + ParseKeyValueFile(base::FilePath("/etc/os-release")); + + default_product_ = GetValue(os_release_values, "GOOGLE_CRASH_ID", ""); + if (default_product_.empty()) + default_product_ = GetValue(os_release_values, "ID", ""); + + default_version_ = GetValue(os_release_values, "GOOGLE_CRASH_VERSION_ID", ""); + if (default_version_.empty()) + default_version_ = GetValue(os_release_values, "VERSION_ID", ""); + + return ReapplyConfig(config_); +} + +void CrashSenderService::Restart(const CrashSenderConfiguration& config) { + CrashSenderConfiguration old_config = config_; + config_ = config; + if (!ReapplyConfig(config_)) { + LOG(ERROR) << "Restarting failed. Reapplying old configuration."; + config_ = old_config; + CHECK(ReapplyConfig(config_)); + } +} + +CrashSenderConfiguration CrashSenderService::ParseConfiguration( + const base::FilePath& config_file) { + CrashSenderConfiguration result; + std::map key_values = + ParseKeyValueFile(config_file); + + result.force_official = GetIntValue(key_values, kForceOfficial, false); + result.max_crash_rate = GetIntValue(key_values, kMaxCrashRate, 32); + result.mock_developer_mode = + GetIntValue(key_values, kMockDeveloperMode, false); + result.override_pause_sending = + GetIntValue(key_values, kOverridePauseSending, false); + result.report_upload_prod_url = + GetValue(key_values, kReportUploadProdUrl, + "https://clients2.google.com/cr/report"); + result.seconds_send_spread = GetIntValue(key_values, kSecondsSendSpread, 600); + + return result; +} + +bool CrashSenderService::ReapplyConfig(const CrashSenderConfiguration& config) { + bool test_run = IsMock(); + const char* lock_path = + test_run ? kCrashSenderLockForTestsPath : kCrashSenderLockPath; + lock_file_.reset( + new base::File(base::FilePath(lock_path), base::File::FLAG_OPEN_ALWAYS | + base::File::FLAG_READ | + base::File::FLAG_WRITE)); + if (lock_file_->Lock() != base::File::FILE_OK) { + LOG(ERROR) << "Already running; quitting."; + return false; + } + base::FilePath run_file(kRunFilePath); + run_file_deleter_.Reset( + base::Bind(base::IgnoreResult(&base::DeleteFile), run_file, false)); + std::string pid = base::IntToString(getpid()) + "\n"; + base::WriteFile(run_file, pid.data(), pid.length()); + + CollectAllCrashes(); + if (test_run) { + base::MessageLoop::current()->PostTask(FROM_HERE, + base::MessageLoop::QuitClosure()); + LOG(INFO) << "crash_sender done."; + } + return true; +} + +bool CrashSenderService::IsCrashTestInProgress() const { + return base::PathExists(base::FilePath(kCrashTestInProgressPath)); +} + +bool CrashSenderService::IsTestImage() const { + if (IsCrashTestInProgress()) + return false; + + return StartsWithASCII(channel_, "test", true); +} + +bool CrashSenderService::IsMock() const { + return base::PathExists(base::FilePath(kMockCrashSendingPath)); +} + +bool CrashSenderService::IsMockSuccessful() const { + std::string content; + if (base::ReadFileToString(base::FilePath(kMockCrashSendingPath), &content)) + return content.empty(); + + return false; +} + +bool CrashSenderService::IsOfficialImage() const { + return config_.force_official || official_; +} + +bool CrashSenderService::IsDeveloperMode() const { + if (config_.mock_developer_mode) + return true; + + if (IsCrashTestInProgress()) + return false; + + return VbGetSystemPropertyInt("devsw_boot"); +} + +bool CrashSenderService::IsDeveloperImage() const { + // Mirrors crash_collector.cc:CrashCollector::IsDeveloperImage(). + if (IsCrashTestInProgress()) + return false; + + return base::PathExists(base::FilePath(kLeaveCorePath)); +} + +std::string CrashSenderService::GetHardwareClass() const { + std::string content; + if (base::ReadFileToString(base::FilePath(kHWClassPath), &content)) + return content; + + char buffer[VB_MAX_STRING_PROPERTY]; + const char* hwid = + VbGetSystemPropertyString("hwid", buffer, VB_MAX_STRING_PROPERTY); + if (hwid) + return hwid; + + return "undefined"; +} + +std::string CrashSenderService::GetConsentId() const { + std::string content; + if (base::ReadFileToString(base::FilePath(kConsentIdPath), &content)) { + content.erase(std::remove(content.begin(), content.end(), '-'), + content.end()); + return content; + } + + return "undefined"; +} + +void CrashSenderService::CollectCrashes(const base::FilePath& dir) { + std::vector> files = + GetOrderedFiles(dir); + base::Time now = base::Time::Now(); + for (const std::pair& file : files) { + if (file.second.FinalExtension() == ".meta") { + MetaFile info; + info.modification_time = file.first; + info.path = file.second; + info.meta_information = ParseKeyValueFile(info.path); + switch (FilterCrashes(info)) { + case CAN_UPLOAD: + current_crashes_.push_back(info); + break; + case DELETE: + RemoveReport(info.path); + break; + case WAIT: + // Nothing + break; + } + } else if ((now - file.first >= base::TimeDelta::FromDays(1)) && + !base::PathExists(file.second.ReplaceExtension(".meta"))) { + if (base::DeleteFile(file.second, false)) { + LOG(INFO) << "Removing old orphaned file: " << file.second.value(); + } else { + LOG(WARNING) << "Unable to delete: " << file.second.value(); + } + } + } +} + +void CrashSenderService::CollectAllCrashes() { + current_crashes_.clear(); + + std::vector crash_directories = GetCrashDirectories(); + for (const base::FilePath& path : crash_directories) { + CrashSenderService::CollectCrashes(path); + } + std::sort(current_crashes_.begin(), current_crashes_.end(), + &order_meta_files); + + if (current_crashes_.empty()) { + // If no crash is present, wait for an hour. + ScheduleNext(); + return; + } + + PrepareToSendNextCrash(); +} + +CrashSenderService::FileStatus CrashSenderService::FilterCrashes( + const MetaFile& file) { + if (!metrics_lib_.AreMetricsEnabled()) { + LOG(INFO) << "Crash reporting is disabled. Removing crash."; + return DELETE; + } + + if (!IsMock() && !IsOfficialImage()) { + LOG(INFO) << "Not an official OS version. Removing crash."; + return DELETE; + } + + if (GetValue(file.meta_information, "done", "") != "1") { + // This report is incomplete, so if it's old, just remove it + if (base::Time::Now() - file.modification_time >= + base::TimeDelta::FromDays(1)) { + LOG(INFO) << "Removing old incomplete metadata."; + return DELETE; + } else { + LOG(INFO) << "Ignoring recent incomplete metadata."; + return WAIT; + } + } + + std::string report_kind = GetKind(file); + if (report_kind != "minidump" && report_kind != "kcrash" && + report_kind != "log") { + LOG(INFO) << "Unknown report kind " << report_kind << ". Removing report."; + return DELETE; + } + + return CAN_UPLOAD; +} + +bool CrashSenderService::MustThrottle() const { + base::FilePath timestamps_dir(kTimestampsDirPath); + if (!base::CreateDirectoryAndGetError(timestamps_dir, nullptr)) { + LOG(WARNING) << "Unable to create directory: " << timestamps_dir.value(); + return true; + } + + base::Time now = base::Time::Now(); + base::FileEnumerator timestamps(timestamps_dir, false, + base::FileEnumerator::FILES); + int sends_in_24hrs = 0; + for (base::FilePath file = timestamps.Next(); !file.empty(); + file = timestamps.Next()) { + if (now - timestamps.GetInfo().GetLastModifiedTime() >= + base::TimeDelta::FromDays(1)) { + base::DeleteFile(file, false); + } else { + ++sends_in_24hrs; + } + } + LOG(INFO) << "Current send rate: " << sends_in_24hrs << "sends/24hrs"; + if (sends_in_24hrs >= config_.max_crash_rate) { + LOG(INFO) << "Cannot send more crashes: current " << sends_in_24hrs + << "send/24hrs >= max " << config_.max_crash_rate << "send/24hrs"; + return true; + } + base::FilePath tmp_file; + if (!base::CreateTemporaryFileInDir(timestamps_dir, &tmp_file)) { + LOG(WARNING) << "Unable to create a file in " << timestamps_dir.value(); + return true; + } + return false; +} + +void CrashSenderService::PrepareToSendNextCrash() { + // If we cannot send any crashes, wait one hour. + if (!CanSendNextCrash()) { + ScheduleNext(); + return; + } + + // If there is no crash to send, collect crashes and return. + if (current_crashes_.empty()) { + CollectAllCrashes(); + return; + } + + const MetaFile& file = current_crashes_.front(); + base::TimeDelta time_to_wait = + std::max(file.modification_time + base::TimeDelta::FromSeconds(30) - + base::Time::Now(), + base::TimeDelta::FromSeconds( + base::RandInt(1, config_.seconds_send_spread))); + LOG(INFO) << "Scheduled to send " << file.path.value() << " in " + << time_to_wait.InSeconds() << "s."; + + if (IsMock()) { + SendNextCrash(); + } else { + timer_.Start(FROM_HERE, time_to_wait, this, + &CrashSenderService::SendNextCrash); + } +} + +bool CrashSenderService::CanSendNextCrash() { + // Handle pause crash sending + base::FilePath pause_crash_sending(kPauseCrashSendingPath); + if (base::PathExists(pause_crash_sending) && + !config_.override_pause_sending) { + LOG(INFO) << "Not sending crashes due to " << pause_crash_sending.value(); + return false; + } + + // Handle is test image + if (IsTestImage()) { + LOG(INFO) << "Not sending crashes due to test image."; + return false; + } + + // Handle certificate path + base::FilePath restricted_certificates_path(kRestrictedCertificatesPath); + if (!base::DirectoryExists(restricted_certificates_path)) { + LOG(INFO) << "Not sending crashes due to " + << restricted_certificates_path.value() << " missing."; + return false; + } + + // Guest mode. + if (metrics_lib_.IsGuestMode()) { + LOG(INFO) + << "Guest mode has been entered. Delaying crash sending until exited."; + return false; + } + + return true; +} + +void CrashSenderService::SendNextCrash() { + // Ensure the timer will be called again if this is exited early due to + // exceptional conditions. + ScheduleNext(); + + if (!CanSendNextCrash()) + return; + + // Check uploads rate + if (MustThrottle()) { + LOG(INFO) << "Sending a report would exceed rate. Leaving for later."; + return; + } + + const MetaFile file = current_crashes_[0]; + current_crashes_.erase(current_crashes_.begin()); + + // Trying to send a crash. Preparing next crash. + base::ScopedClosureRunner send_next_crash(base::Bind( + &CrashSenderService::PrepareToSendNextCrash, base::Unretained(this))); + // Delete the report whatever the result. + base::ScopedClosureRunner report_delete(base::Bind(&RemoveReport, file.path)); + + ScopedCurl curl(IsMock()); + + LOG(INFO) << "Sending crash:"; + std::string product = GetValue(file.meta_information, "upload_var_prod", ""); + if (product.empty()) + product = default_product_; + + if (product.empty()) + product = kDefaultProduct; + + curl.AddMultipartContent("prod", product); + if (product != kDefaultProduct) + LOG(INFO) << "Sending crash report on behalf of " << product; + + LOG(INFO) << "Metadata: " << file.path.value() << " (" << GetKind(file) + << ")"; + + std::string version = GetValue(file.meta_information, "upload_var_ver", ""); + if (version.empty()) + version = default_version_; + if (version.empty()) + version = GetValue(file.meta_information, "ver", ""); + + curl.AddMultipartContent("ver", version); + curl.AddMultipartContent("board", board_); + curl.AddMultipartContent("hwclass", GetHardwareClass()); + curl.AddMultipartContent( + "exec_name", GetValue(file.meta_information, "exec_name", "undefined")); + + std::string image_type; + if (IsTestImage()) { + image_type = "test"; + } else if (IsDeveloperImage()) { + image_type = "dev"; + } else if (config_.force_official) { + image_type = "force-official"; + } else if (IsMock() && !IsMockSuccessful()) { + image_type = "mock-fail"; + } + if (!image_type.empty()) + curl.AddMultipartContent("image_type", image_type); + + if (VbGetSystemPropertyInt("cros_debug") && IsDeveloperMode()) + curl.AddMultipartContent("boot_mode", "dev"); + + std::string error_type = GetValue(file.meta_information, "error_type", ""); + if (!error_type.empty()) + curl.AddMultipartContent("error_type", error_type); + + curl.AddMultipartContent("guid", GetConsentId()); + curl.AddMultipartContent( + "write_payload_size", + GetValue(file.meta_information, "payload_size", "undefined")); + + base::FilePath payload = + GetPathValue(file.meta_information, "payload", base::FilePath()); + if (!payload.value().empty()) { + int64 file_size; + if (base::GetFileSize(payload, &file_size)) { + curl.AddMultipartContent("send_payload_size", + base::Int64ToString(file_size)); + curl.AddFile("upload_file_" + GetKind(file), payload); + } + } + + std::string signature = GetValue(file.meta_information, "sig", ""); + if (!signature.empty()) { + curl.AddMultipartContent("sig", signature); + curl.AddMultipartContent("sig2", signature); + } + + base::FilePath log = + GetPathValue(file.meta_information, "log", base::FilePath()); + if (base::PathExists(log)) + curl.AddFile("log", log); + + std::string upload_prefix = + GetValue(file.meta_information, "upload_prefix", ""); + + const char kUploadVarPrefix[] = "upload_var_"; + const char kUploadFilePrefix[] = "upload_file_"; + for (const auto& pair : file.meta_information) { + if (StartsWithASCII(pair.first, kUploadVarPrefix, true)) { + curl.AddMultipartContent( + upload_prefix + pair.first.substr(arraysize(kUploadVarPrefix) - 1), + pair.second); + } + if (StartsWithASCII(pair.first, kUploadFilePrefix, true)) { + curl.AddFile( + upload_prefix + pair.first.substr(arraysize(kUploadFilePrefix) - 1), + base::FilePath(pair.second)); + } + } + + if (IsMock()) { + if (IsMockSuccessful()) { + LOG(INFO) << "Mocking successful send"; + } else { + LOG(INFO) << "Mocking unsuccessful send"; + } + return; + } + + curl_easy_setopt(curl.curl(), CURLOPT_URL, + config_.report_upload_prod_url.c_str()); + curl_easy_setopt(curl.curl(), CURLOPT_POST, 1L); + std::vector proxies = proxy_resolver_->GetProxiesForUrl( + config_.report_upload_prod_url, base::TimeDelta::FromSeconds(5)); + if (proxies.size() && proxies[0] != "direct://") + curl_easy_setopt(curl.curl(), CURLOPT_PROXY, proxies[0].c_str()); + + // TODO(qsr) Remove + curl_easy_setopt(curl.curl(), CURLOPT_PROXY, "http://192.168.45.1:8888"); + + std::string received_data = ""; + base::Callback callback = + base::Bind(&AppendDataToString, &received_data); + curl_easy_setopt(curl.curl(), CURLOPT_WRITEFUNCTION, &CurlWriteData); + curl_easy_setopt(curl.curl(), CURLOPT_WRITEDATA, &callback); + + CURLcode success = curl.perform(); + + if (success != 0 || received_data.size() > 20) { + LOG(ERROR) << "Unable to upload crash report. Error code: " << success; + return; + } + + std::string product_name; + if (product == "Chrome_ChromeOS") { + if (IsOfficialImage()) { + product_name = "Chrome"; + } else { + product_name = "Chromium"; + } + } else { + if (IsOfficialImage()) { + product_name = "ChromeOS"; + } else { + product_name = "ChromiumOS"; + } + } + std::string log_string = base::StringPrintf( + "%" PRIu64 ",%s,%s\n", static_cast(base::Time::Now().ToTimeT()), + received_data.c_str(), product_name.c_str()); + if (base::AppendToFile(base::FilePath(kChromeCrashLogPath), log_string.data(), + log_string.size()) == -1) { + LOG(ERROR) << "Unable to update crash log."; + } +} + +void CrashSenderService::ScheduleNext() { + timer_.Start(FROM_HERE, base::TimeDelta::FromHours(1), this, + &CrashSenderService::PrepareToSendNextCrash); +} + +DbusCrashSenderServiceImpl::DbusCrashSenderServiceImpl( + const CrashSenderConfiguration& config) + : CrashSenderService(config) {} + +DbusCrashSenderServiceImpl::~DbusCrashSenderServiceImpl() {} + +bool DbusCrashSenderServiceImpl::Start(dbus::Bus* bus) { + if (!bus || !bus->Connect()) { + LOG(ERROR) << "Failed to connect to DBus"; + return false; + } + + bus_ = bus; + proxy_resolver_.reset(new DBusProxyResolver(bus_)); + proxy_resolver_->Init(); + return CrashSenderService::Start(proxy_resolver_.get()); +} + +} // namespace crash_reporter diff --git a/crash_reporter/crash_sender_service.h b/crash_reporter/crash_sender_service.h new file mode 100644 index 000000000..bc2b35c94 --- /dev/null +++ b/crash_reporter/crash_sender_service.h @@ -0,0 +1,114 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRASH_REPORTER_CRASH_SENDER_SERVICE_H_ +#define CRASH_REPORTER_CRASH_SENDER_SERVICE_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "crash-reporter/proxy_resolver.h" +#include "metrics/metrics_library.h" + +namespace dbus { +class Bus; +} // namespace dbus + +namespace crash_reporter { + +// The configuration for the crash sender. See |crash_sender.conf| for details. +struct CrashSenderConfiguration { + bool force_official; + int max_crash_rate; + bool mock_developer_mode; + bool override_pause_sending; + std::string report_upload_prod_url; + int seconds_send_spread; +}; + +// The information about a crash report, which is obtained from the associated +// meta file. +struct MetaFile { + base::Time modification_time; + base::FilePath path; + std::map meta_information; +}; + +class CrashSenderService { + public: + explicit CrashSenderService(const CrashSenderConfiguration& config); + virtual ~CrashSenderService(); + + bool Start(ProxyResolver* proxy_resolver); + + void Restart(const CrashSenderConfiguration& config); + + static CrashSenderConfiguration ParseConfiguration( + const base::FilePath& config_file); + + private: + enum FileStatus { + CAN_UPLOAD, + WAIT, + DELETE, + }; + + bool ReapplyConfig(const CrashSenderConfiguration& config); + bool IsCrashTestInProgress() const; + bool IsTestImage() const; + bool IsMock() const; + bool IsMockSuccessful() const; + bool IsOfficialImage() const; + bool IsDeveloperMode() const; + bool IsDeveloperImage() const; + std::string GetHardwareClass() const; + std::string GetConsentId() const; + void CollectCrashes(const base::FilePath& dir); + void CollectAllCrashes(); + FileStatus FilterCrashes(const MetaFile& file); + bool MustThrottle() const; + void PrepareToSendNextCrash(); + bool CanSendNextCrash(); + void SendNextCrash(); + void ScheduleNext(); + + ProxyResolver* proxy_resolver_ = nullptr; + CrashSenderConfiguration config_; + MetricsLibrary metrics_lib_; + base::OneShotTimer timer_; + base::ScopedClosureRunner run_file_deleter_; + scoped_ptr lock_file_; + std::string channel_; + std::string board_; + std::string default_product_; + std::string default_version_; + bool official_ = false; + std::vector current_crashes_; + + DISALLOW_COPY_AND_ASSIGN(CrashSenderService); +}; + +class DbusCrashSenderServiceImpl : public CrashSenderService { + public: + explicit DbusCrashSenderServiceImpl(const CrashSenderConfiguration& config); + virtual ~DbusCrashSenderServiceImpl(); + + bool Start(dbus::Bus* bus); + + private: + dbus::Bus* bus_ = nullptr; + scoped_ptr proxy_resolver_; + + DISALLOW_COPY_AND_ASSIGN(DbusCrashSenderServiceImpl); +}; +} // namespace crash_reporter + +#endif // CRASH_REPORTER_CRASH_SENDER_SERVICE_H_ diff --git a/crash_reporter/init/crash-sender.conf b/crash_reporter/init/crash-sender.conf index 892186f91..11b7e4fd1 100644 --- a/crash_reporter/init/crash-sender.conf +++ b/crash_reporter/init/crash-sender.conf @@ -1,11 +1,12 @@ -# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +# Copyright 2014 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -description "Run the crash sender periodically" -author "chromium-os-dev@chromium.org" +description "Runs a daemon which send collected crash reports." +author "chromium-os-dev@chromium.org" -start on starting system-services +start on started system-services stop on stopping system-services +respawn -exec periodic_scheduler 3600 14400 crash_sender /sbin/crash_sender +exec crash_sender diff --git a/crash_reporter/libproxies.cc b/crash_reporter/libproxies.cc new file mode 100644 index 000000000..cb77295ab --- /dev/null +++ b/crash_reporter/libproxies.cc @@ -0,0 +1,57 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crash-reporter/libproxies.h" + +#include + +#include +#include +#include + +namespace crash_reporter { + +const char kLibCrosProxyResolveSignalInterface[] = + "org.chromium.CrashReporterLibcrosProxyResolvedInterface"; +const char kLibCrosProxyResolveName[] = "ProxyResolved"; +const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface"; +const char kLibCrosServiceName[] = "org.chromium.LibCrosService"; +const char kLibCrosServicePath[] = "/org/chromium/LibCrosService"; +const char kLibCrosServiceResolveNetworkProxyMethodName[] = + "ResolveNetworkProxy"; +const char kNoProxy[] = "direct://"; + +std::vector ParseProxyString(const std::string& input) { + std::vector ret; + // Some of this code taken from + // https://chromium.googlesource.com/chromium/chromium/+/master/net/proxy + for (const std::string& token : chromeos::string_utils::Split(input, ';')) { + auto space = + std::find_if(token.begin(), token.end(), IsAsciiWhitespace); + std::string scheme(token.begin(), space); + base::StringToLowerASCII(&scheme); + // Chrome uses "socks" to mean socks4 and "proxy" to mean http. + if (scheme == "socks") { + scheme += "4"; + } else if (scheme == "proxy") { + scheme = "http"; + } else if (scheme != "https" && scheme != "socks4" && scheme != "socks5" && + scheme != "direct") { + continue; // Invalid proxy scheme + } + + std::string host_and_port = std::string(space, token.end()); + base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port); + if (scheme != "direct" && host_and_port.empty()) + continue; // Must supply host/port when non-direct proxy used. + + ret.push_back(scheme + "://" + host_and_port); + } + if (ret.empty() || ret.back() != kNoProxy) + ret.push_back(kNoProxy); + + return ret; +} + +} // namespace crash_reporter diff --git a/crash_reporter/libproxies.h b/crash_reporter/libproxies.h new file mode 100644 index 000000000..ed32f34d0 --- /dev/null +++ b/crash_reporter/libproxies.h @@ -0,0 +1,28 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRASH_REPORTER_LIBPROXIES_H_ +#define CRASH_REPORTER_LIBPROXIES_H_ + +#include +#include + +namespace crash_reporter { + +extern const char kLibCrosProxyResolveSignalInterface[]; +extern const char kLibCrosProxyResolveName[]; +extern const char kLibCrosServiceInterface[]; +extern const char kLibCrosServiceName[]; +extern const char kLibCrosServicePath[]; +extern const char kLibCrosServiceResolveNetworkProxyMethodName[]; +extern const char kNoProxy[]; + +// Copied from src/update_engine/chrome_browser_proxy_resolver.cc +// Parses the browser's answer for resolved proxies. It returns a +// list of strings, each of which is a resolved proxy. +std::vector ParseProxyString(const std::string& input); + +} // namespace crash_reporter + +#endif // CRASH_REPORTER_LIBPROXIES_H_ diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc deleted file mode 100644 index 282c6ae02..000000000 --- a/crash_reporter/list_proxies.cc +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include -#include // for isatty() - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -const char kLibCrosProxyResolveSignalInterface[] = - "org.chromium.CrashReporterLibcrosProxyResolvedInterface"; -const char kLibCrosProxyResolveName[] = "ProxyResolved"; -const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface"; -const char kLibCrosServiceName[] = "org.chromium.LibCrosService"; -const char kLibCrosServicePath[] = "/org/chromium/LibCrosService"; -const char kLibCrosServiceResolveNetworkProxyMethodName[] = - "ResolveNetworkProxy"; -const char kNoProxy[] = "direct://"; - -namespace switches { - -const unsigned kTimeoutDefault = 5; - -const char kHelp[] = "help"; -const char kQuiet[] = "quiet"; -const char kTimeout[] = "timeout"; -const char kVerbose[] = "verbose"; -// Help message to show when the --help command line switch is specified. -const char kHelpMessage[] = - "Chromium OS Crash helper: proxy lister\n" - "\n" - "Available Switches:\n" - " --quiet Only print the proxies\n" - " --verbose Print additional messages even when not run from a TTY\n" - " --timeout=N Set timeout for browser resolving proxies (default is 5)\n" - " --help Show this help.\n"; - -} // namespace switches - -static const char *GetGErrorMessage(const GError *error) { - if (!error) - return "Unknown error."; - return error->message; -} - -// Copied from src/update_engine/chrome_browser_proxy_resolver.cc -// Parses the browser's answer for resolved proxies. It returns a -// list of strings, each of which is a resolved proxy. -std::deque ParseProxyString(const std::string &input) { - std::deque ret; - // Some of this code taken from - // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and - // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc - base::StringTokenizer entry_tok(input, ";"); - while (entry_tok.GetNext()) { - std::string token = entry_tok.token(); - base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token); - - // Start by finding the first space (if any). - std::string::iterator space; - for (space = token.begin(); space != token.end(); ++space) { - if (IsAsciiWhitespace(*space)) { - break; - } - } - - std::string scheme = std::string(token.begin(), space); - base::StringToLowerASCII(&scheme); - // Chrome uses "socks" to mean socks4 and "proxy" to mean http. - if (scheme == "socks") - scheme += "4"; - else if (scheme == "proxy") - scheme = "http"; - else if (scheme != "https" && - scheme != "socks4" && - scheme != "socks5" && - scheme != "direct") - continue; // Invalid proxy scheme - - std::string host_and_port = std::string(space, token.end()); - base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port); - if (scheme != "direct" && host_and_port.empty()) - continue; // Must supply host/port when non-direct proxy used. - ret.push_back(scheme + "://" + host_and_port); - } - if (ret.empty() || *ret.rbegin() != kNoProxy) - ret.push_back(kNoProxy); - return ret; -} - -// Define a signal-watcher class to handle the D-Bus signal sent to us when -// the browser answers our request to resolve proxies. -class BrowserProxyResolvedSignalWatcher : public chromeos::dbus::SignalWatcher { - public: - explicit BrowserProxyResolvedSignalWatcher(GMainLoop *main_loop, - std::deque *proxies) - : main_loop_(main_loop), proxies_(proxies) { } - - void OnSignal(DBusMessage *message) override { - // Get args - char *source_url = NULL; - char *proxy_list = NULL; - char *error = NULL; - DBusError arg_error; - dbus_error_init(&arg_error); - if (!dbus_message_get_args(message, &arg_error, - DBUS_TYPE_STRING, &source_url, - DBUS_TYPE_STRING, &proxy_list, - DBUS_TYPE_STRING, &error, - DBUS_TYPE_INVALID)) { - LOG(ERROR) << "Error reading D-Bus signal"; - return; - } - if (!source_url || !proxy_list) { - LOG(ERROR) << "Error getting url, proxy list from D-Bus signal"; - return; - } - - const std::deque &proxies = ParseProxyString(proxy_list); - for (std::deque::const_iterator it = proxies.begin(); - it != proxies.end(); ++it) { - LOG(INFO) << "Found proxy via browser signal: " << (*it).c_str(); - proxies_->push_back(*it); - } - - g_main_loop_quit(main_loop_); - } - - private: - GMainLoop *main_loop_; - std::deque *proxies_; -}; - -static gboolean HandleBrowserTimeout(void *data) { - GMainLoop *main_loop = reinterpret_cast(data); - LOG(ERROR) << "Timeout while waiting for browser to resolve proxy"; - g_main_loop_quit(main_loop); - return false; // only call once -} - -static bool ShowBrowserProxies(std::string url, unsigned timeout) { - GMainLoop *main_loop = g_main_loop_new(NULL, false); - - chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); - if (!dbus.HasConnection()) { - LOG(ERROR) << "Error connecting to system D-Bus"; - return false; - } - chromeos::dbus::Proxy browser_proxy(dbus, - kLibCrosServiceName, - kLibCrosServicePath, - kLibCrosServiceInterface); - if (!browser_proxy) { - LOG(ERROR) << "Error creating D-Bus proxy to interface " - << "'" << kLibCrosServiceName << "'"; - return false; - } - - // Watch for a proxy-resolved signal sent to us - std::deque proxies; - BrowserProxyResolvedSignalWatcher proxy_resolver(main_loop, &proxies); - proxy_resolver.StartMonitoring(kLibCrosProxyResolveSignalInterface, - kLibCrosProxyResolveName); - - // Request the proxies for our URL. The answer is sent to us via a - // proxy-resolved signal. - GError *gerror = NULL; - if (!dbus_g_proxy_call(browser_proxy.gproxy(), - kLibCrosServiceResolveNetworkProxyMethodName, - &gerror, - G_TYPE_STRING, url.c_str(), - G_TYPE_STRING, kLibCrosProxyResolveSignalInterface, - G_TYPE_STRING, kLibCrosProxyResolveName, - G_TYPE_INVALID, G_TYPE_INVALID)) { - LOG(ERROR) << "Error performing D-Bus proxy call " - << "'" << kLibCrosServiceResolveNetworkProxyMethodName << "'" - << ": " << GetGErrorMessage(gerror); - return false; - } - - // Setup a timeout in case the browser doesn't respond with our signal - g_timeout_add_seconds(timeout, &HandleBrowserTimeout, main_loop); - - // Loop until we either get the proxy-resolved signal, or until the - // timeout is reached. - g_main_loop_run(main_loop); - - // If there are no proxies, then we failed to get the proxy-resolved - // signal (e.g. timeout was reached). - if (proxies.empty()) - return false; - - for (std::deque::const_iterator it = proxies.begin(); - it != proxies.end(); ++it) { - printf("%s\n", (*it).c_str()); - } - return true; -} - -int main(int argc, char *argv[]) { - CommandLine::Init(argc, argv); - CommandLine* cl = CommandLine::ForCurrentProcess(); - - if (cl->HasSwitch(switches::kHelp)) { - LOG(INFO) << switches::kHelpMessage; - return 0; - } - - bool quiet = cl->HasSwitch(switches::kQuiet); - bool verbose = cl->HasSwitch(switches::kVerbose); - - unsigned timeout = switches::kTimeoutDefault; - std::string str_timeout = cl->GetSwitchValueASCII(switches::kTimeout); - if (!str_timeout.empty() && !base::StringToUint(str_timeout, &timeout)) { - LOG(ERROR) << "Invalid timeout value: " << str_timeout; - return 1; - } - - // Default to logging to syslog. - int init_flags = chromeos::kLogToSyslog; - // Log to stderr if a TTY (and "-quiet" wasn't passed), or if "-verbose" - // was passed. - - if ((!quiet && isatty(STDERR_FILENO)) || verbose) - init_flags |= chromeos::kLogToStderr; - chromeos::InitLog(init_flags); - - ::g_type_init(); - - std::string url; - CommandLine::StringVector urls = cl->GetArgs(); - if (!urls.empty()) { - url = urls[0]; - LOG(INFO) << "Resolving proxies for URL: " << url; - } else { - LOG(INFO) << "Resolving proxies without URL"; - } - - if (!ShowBrowserProxies(url, timeout)) { - LOG(ERROR) << "Error resolving proxies via the browser"; - LOG(INFO) << "Assuming direct proxy"; - printf("%s\n", kNoProxy); - } - - return 0; -} diff --git a/crash_reporter/proxy_resolver.cc b/crash_reporter/proxy_resolver.cc new file mode 100644 index 000000000..a4cc73247 --- /dev/null +++ b/crash_reporter/proxy_resolver.cc @@ -0,0 +1,41 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crash-reporter/proxy_resolver.h" + +#include + +#include "crash-reporter/libproxies.h" + +namespace crash_reporter { + +DBusProxyResolver::DBusProxyResolver(dbus::Bus* bus) : bus_(bus) {} + +void DBusProxyResolver::Init() { + lib_cros_service_proxy_ = bus_->GetObjectProxy( + kLibCrosServiceName, dbus::ObjectPath(kLibCrosServicePath)); + if (!lib_cros_service_proxy_) { + LOG(WARNING) << "Unable to connect to LibCrosService."; + } +} + +std::vector DBusProxyResolver::GetProxiesForUrl( + const std::string& url, const base::TimeDelta& timeout) { + if (!lib_cros_service_proxy_) return {kNoProxy}; + + auto response = chromeos::dbus_utils::CallMethodAndBlockWithTimeout( + timeout.InMilliseconds(), lib_cros_service_proxy_, + kLibCrosProxyResolveSignalInterface, + kLibCrosServiceResolveNetworkProxyMethodName, url); + if (response) { + std::string returned_message; + if (chromeos::dbus_utils::ExtractMethodCallResults(response.get(), nullptr, + &returned_message)) { + return ParseProxyString(returned_message); + } + } + return {kNoProxy}; +} + +} // namespace crash_reporter diff --git a/crash_reporter/proxy_resolver.h b/crash_reporter/proxy_resolver.h new file mode 100644 index 000000000..a6d1989ca --- /dev/null +++ b/crash_reporter/proxy_resolver.h @@ -0,0 +1,45 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRASH_REPORTER_PROXY_RESOLVER_H_ +#define CRASH_REPORTER_PROXY_RESOLVER_H_ + +#include +#include + +#include +#include + +namespace base { +class TimeDelta; +} // namespace base + +namespace crash_reporter { + +class ProxyResolver { + public: + virtual ~ProxyResolver() {} + + virtual std::vector GetProxiesForUrl( + const std::string& url, const base::TimeDelta& timeout) = 0; +}; + +class DBusProxyResolver : public ProxyResolver { + public: + explicit DBusProxyResolver(dbus::Bus* bus); + ~DBusProxyResolver() override = default; + + void Init(); + + std::vector GetProxiesForUrl( + const std::string& url, const base::TimeDelta& timeout) override; + + private: + scoped_refptr bus_; + scoped_refptr lib_cros_service_proxy_; +}; + +} // namespace crash_reporter + +#endif // CRASH_REPORTER_PROXY_RESOLVER_H_ From c8cb4ac7ba5f51a32a58ac65f3c3481c249e5ff5 Mon Sep 17 00:00:00 2001 From: Benjamin Lerman Date: Thu, 11 Sep 2014 12:43:41 +0000 Subject: [PATCH 199/225] Revert "crash-reporter: Replace the crash_sender script by a service daemon." This reverts commit 38c5ad3a94762945456f8e25c78f464d9221bb84. Was think it will go to the CQ. Change-Id: Icbe21da107375b0975f6328623fd9cdd38f71b09 Reviewed-on: https://chromium-review.googlesource.com/217740 Reviewed-by: Benjamin Lerman Tested-by: Benjamin Lerman --- crash_reporter/crash-reporter.gyp | 40 +- crash_reporter/crash_sender | 685 ++++++++++++++++++++ crash_reporter/crash_sender.conf | 28 - crash_reporter/crash_sender_daemon.cc | 101 --- crash_reporter/crash_sender_daemon.h | 43 -- crash_reporter/crash_sender_service.cc | 853 ------------------------- crash_reporter/crash_sender_service.h | 114 ---- crash_reporter/init/crash-sender.conf | 11 +- crash_reporter/libproxies.cc | 57 -- crash_reporter/libproxies.h | 28 - crash_reporter/list_proxies.cc | 257 ++++++++ crash_reporter/proxy_resolver.cc | 41 -- crash_reporter/proxy_resolver.h | 45 -- 13 files changed, 950 insertions(+), 1353 deletions(-) create mode 100755 crash_reporter/crash_sender delete mode 100644 crash_reporter/crash_sender.conf delete mode 100644 crash_reporter/crash_sender_daemon.cc delete mode 100644 crash_reporter/crash_sender_daemon.h delete mode 100644 crash_reporter/crash_sender_service.cc delete mode 100644 crash_reporter/crash_sender_service.h delete mode 100644 crash_reporter/libproxies.cc delete mode 100644 crash_reporter/libproxies.h create mode 100644 crash_reporter/list_proxies.cc delete mode 100644 crash_reporter/proxy_resolver.cc delete mode 100644 crash_reporter/proxy_resolver.h diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index f36d1d0f1..79faf97e5 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -63,51 +63,17 @@ ], }, { - 'target_name': 'libproxies', - 'type': 'static_library', - 'variables': { - 'exported_deps': [ - 'libchrome-<(libbase_ver)', - ], - 'deps': ['<@(exported_deps)'], - }, - 'all_dependent_settings': { - 'variables': { - 'deps': [ - '<@(exported_deps)', - ], - }, - }, - 'sources': [ - 'libproxies.cc', - 'libproxies.h', - ], - }, - { - 'target_name': 'crash_sender', + 'target_name': 'list_proxies', 'type': 'executable', 'variables': { 'deps': [ 'dbus-1', + 'dbus-glib-1', 'libchrome-<(libbase_ver)', - 'libchromeos-<(libbase_ver)', - 'libcurl', - 'libmetrics-<(libbase_ver)', ], }, - 'dependencies': [ - 'libproxies', - ], - 'libraries': [ - '-lvboot_host', - ], 'sources': [ - 'crash_sender_daemon.cc', - 'crash_sender_daemon.h', - 'crash_sender_service.cc', - 'crash_sender_service.h', - 'proxy_resolver.cc', - 'proxy_resolver.h', + 'list_proxies.cc', ], }, { diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender new file mode 100755 index 000000000..c83a12ae2 --- /dev/null +++ b/crash_reporter/crash_sender @@ -0,0 +1,685 @@ +#!/bin/sh + +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +# Default product ID in crash report (used if GOOGLE_CRASH_* is undefined). +CHROMEOS_PRODUCT=ChromeOS + +# File whose existence implies crash reports may be sent, and whose +# contents includes our machine's anonymized guid. +CONSENT_ID="/home/chronos/Consent To Send Stats" + +# Crash sender lock in case the sender is already running. +CRASH_SENDER_LOCK="/var/lock/crash_sender" + +# Path to file that indicates a crash test is currently running. +CRASH_TEST_IN_PROGRESS_FILE="/tmp/crash-test-in-progress" + +# Path to find which is required for computing the crash rate. +FIND="/usr/bin/find" + +# Set this to 1 in the environment to allow uploading crash reports +# for unofficial versions. +FORCE_OFFICIAL=${FORCE_OFFICIAL:-0} + +# Path to hardware class description. +HWCLASS_PATH="/sys/devices/platform/chromeos_acpi/HWID" + +# Path to file that indicates this is a developer image. +LEAVE_CORE_FILE="/root/.leave_core" + +# Path to list_proxies. +LIST_PROXIES="/usr/bin/list_proxies" + +# Maximum crashes to send per day. +MAX_CRASH_RATE=${MAX_CRASH_RATE:-32} + +# Path to metrics_client. +METRICS_CLIENT="/usr/bin/metrics_client" + +# File whose existence mocks crash sending. If empty we pretend the +# crash sending was successful, otherwise unsuccessful. +MOCK_CRASH_SENDING="/tmp/mock-crash-sending" + +# Set this to 1 in the environment to pretend to have booted in developer +# mode. This is used by autotests. +MOCK_DEVELOPER_MODE=${MOCK_DEVELOPER_MODE:-0} + +# Ignore PAUSE_CRASH_SENDING file if set. +OVERRIDE_PAUSE_SENDING=${OVERRIDE_PAUSE_SENDING:-0} + +# File whose existence causes crash sending to be delayed (for testing). +# Must be stateful to enable testing kernel crashes. +PAUSE_CRASH_SENDING="/var/lib/crash_sender_paused" + +# URL to send official build crash reports to. +REPORT_UPLOAD_PROD_URL="https://clients2.google.com/cr/report" + +# Path to a directory of restricted certificates which includes +# a certificate for ${REPORT_UPLOAD_PROD_URL}. +RESTRICTED_CERTIFICATES_PATH="/usr/share/chromeos-ca-certificates" + +# File whose existence implies we're running and not to start again. +RUN_FILE="/var/run/crash_sender.pid" + +# Maximum time to sleep between sends. +SECONDS_SEND_SPREAD=${SECONDS_SEND_SPREAD:-600} + +# The syslog tag for all logging we emit. +TAG="$(basename $0)[$$]" + +# Directory to store timestamp files indicating the uploads in the past 24 +# hours. +TIMESTAMPS_DIR="/var/lib/crash_sender" + +# Temp directory for this process. +TMP_DIR="" + +# Chrome's crash report log file. +CHROME_CRASH_LOG="/var/log/chrome/Crash Reports/uploads.log" + +lecho() { + logger -t "${TAG}" "$@" +} + +# Returns true if mock is enabled. +is_mock() { + [ -f "${MOCK_CRASH_SENDING}" ] && return 0 + return 1 +} + +is_mock_successful() { + local mock_in=$(cat "${MOCK_CRASH_SENDING}") + [ "${mock_in}" = "" ] && return 0 # empty file means success + return 1 +} + +cleanup() { + if [ -n "${TMP_DIR}" ]; then + rm -rf "${TMP_DIR}" + fi + rm -f "${RUN_FILE}" + crash_done +} + +crash_done() { + if is_mock; then + # For testing purposes, emit a message to log so that we + # know when the test has received all the messages from this run. + lecho "crash_sender done." + fi +} + +is_official_image() { + [ ${FORCE_OFFICIAL} -ne 0 ] && return 0 + grep ^CHROMEOS_RELEASE_DESCRIPTION /etc/lsb-release | grep -q Official +} + +# Returns 0 if the a crash test is currently running. NOTE: Mirrors +# crash_collector.cc:CrashCollector::IsCrashTestInProgress(). +is_crash_test_in_progress() { + [ -f "${CRASH_TEST_IN_PROGRESS_FILE}" ] && return 0 + return 1 +} + +# Returns 0 if we should consider ourselves to be running on a developer +# image. NOTE: Mirrors crash_collector.cc:CrashCollector::IsDeveloperImage(). +is_developer_image() { + # If we're testing crash reporter itself, we don't want to special-case + # for developer images. + is_crash_test_in_progress && return 1 + [ -f "${LEAVE_CORE_FILE}" ] && return 0 + return 1 +} + +# Returns 0 if we should consider ourselves to be running on a test image. +is_test_image() { + # If we're testing crash reporter itself, we don't want to special-case + # for test images. + is_crash_test_in_progress && return 1 + case $(get_channel) in + test*) return 0;; + esac + return 1 +} + +# Returns 0 if the machine booted up in developer mode. +is_developer_mode() { + [ ${MOCK_DEVELOPER_MODE} -ne 0 ] && return 0 + # If we're testing crash reporter itself, we don't want to special-case + # for developer mode. + is_crash_test_in_progress && return 1 + crossystem "devsw_boot?1" # exit status will be accurate +} + +# Generate a uniform random number in 0..max-1. +generate_uniform_random() { + local max=$1 + local random="$(od -An -N4 -tu /dev/urandom)" + echo $((random % max)) +} + +# Check if sending a crash now does not exceed the maximum 24hr rate and +# commit to doing so, if not. +check_rate() { + mkdir -p ${TIMESTAMPS_DIR} + # Only consider minidumps written in the past 24 hours by removing all older. + ${FIND} "${TIMESTAMPS_DIR}" -mindepth 1 -mmin +$((24 * 60)) \ + -exec rm -- '{}' ';' + local sends_in_24hrs=$(echo "${TIMESTAMPS_DIR}"/* | wc -w) + lecho "Current send rate: ${sends_in_24hrs}sends/24hrs" + if [ ${sends_in_24hrs} -ge ${MAX_CRASH_RATE} ]; then + lecho "Cannot send more crashes:" + lecho " current ${sends_in_24hrs}send/24hrs >= " \ + "max ${MAX_CRASH_RATE}send/24hrs" + return 1 + fi + mktemp "${TIMESTAMPS_DIR}"/XXXX > /dev/null + return 0 +} + +# Gets the base part of a crash report file, such as name.01234.5678.9012 from +# name.01234.5678.9012.meta or name.01234.5678.9012.log.tar.xz. We make sure +# "name" is sanitized in CrashCollector::Sanitize to not include any periods. +get_base() { + echo "$1" | cut -d. -f-4 +} + +get_extension() { + local extension="${1##*.}" + local filename="${1%.*}" + # For gzipped file, we ignore .gz and get the real extension + if [ "${extension}" = "gz" ]; then + echo "${filename##*.}" + else + echo "${extension}" + fi +} + +# Return which kind of report the given metadata file relates to +get_kind() { + local payload="$(get_key_value "$1" "payload")" + if [ ! -r "${payload}" ]; then + lecho "Missing payload: ${payload}" + echo "undefined" + return + fi + local kind="$(get_extension "${payload}")" + if [ "${kind}" = "dmp" ]; then + echo "minidump" + return + fi + echo "${kind}" +} + +get_key_value() { + local file="$1" key="$2" value + + if [ -f "${file}" ]; then + # Return the first entry. There shouldn't be more than one anyways. + # Substr at length($1) + 2 skips past the key and following = sign (awk + # uses 1-based indexes), but preserves embedded = characters. + value=$(sed -n "/^${key}[[:space:]]*=/{s:^[^=]*=::p;q}" "${file}") + fi + + echo "${value:-undefined}" +} + +get_keys() { + local file="$1" regex="$2" + + awk -F'[[:space:]=]' -vregex="${regex}" \ + 'match($1, regex) { print $1 }' "${file}" +} + +# Return the board name. +get_board() { + get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_BOARD" +} + +# Return the channel name (sans "-channel" suffix). +get_channel() { + get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_TRACK" | + sed 's:-channel$::' +} + +# Return the hardware class or "undefined". +get_hardware_class() { + if [ -r "${HWCLASS_PATH}" ]; then + cat "${HWCLASS_PATH}" + elif crossystem hwid > /dev/null 2>&1; then + echo "$(crossystem hwid)" + else + echo "undefined" + fi +} + +send_crash() { + local meta_path="$1" + local report_payload="$(get_key_value "${meta_path}" "payload")" + local kind="$(get_kind "${meta_path}")" + local exec_name="$(get_key_value "${meta_path}" "exec_name")" + local url="${REPORT_UPLOAD_PROD_URL}" + local chromeos_version="$(get_key_value "${meta_path}" "ver")" + local board="$(get_board)" + local hwclass="$(get_hardware_class)" + local write_payload_size="$(get_key_value "${meta_path}" "payload_size")" + local log="$(get_key_value "${meta_path}" "log")" + local sig="$(get_key_value "${meta_path}" "sig")" + local send_payload_size="$(stat --printf=%s "${report_payload}" 2>/dev/null)" + local product="$(get_key_value "${meta_path}" "upload_var_prod")" + local version="$(get_key_value "${meta_path}" "upload_var_ver")" + local upload_prefix="$(get_key_value "${meta_path}" "upload_prefix")" + local guid + + set -- \ + -F "write_payload_size=${write_payload_size}" \ + -F "send_payload_size=${send_payload_size}" + if [ "${sig}" != "undefined" ]; then + set -- "$@" \ + -F "sig=${sig}" \ + -F "sig2=${sig}" + fi + if [ -r "${report_payload}" ]; then + set -- "$@" \ + -F "upload_file_${kind}=@${report_payload}" + fi + if [ "${log}" != "undefined" -a -r "${log}" ]; then + set -- "$@" \ + -F "log=@${log}" + fi + + if [ "${upload_prefix}" = "undefined" ]; then + upload_prefix="" + fi + + # Grab any variable that begins with upload_. + local v + for k in $(get_keys "${meta_path}" "^upload_"); do + v="$(get_key_value "${meta_path}" "${k}")" + case ${k} in + # Product & version are handled separately. + upload_var_prod) ;; + upload_var_ver) ;; + upload_var_*) + set -- "$@" -F "${upload_prefix}${k#upload_var_}=${v}" + ;; + upload_file_*) + if [ -r "${v}" ]; then + set -- "$@" -F "${upload_prefix}${k#upload_file_}=@${v}" + fi + ;; + esac + done + + # When uploading Chrome reports we need to report the right product and + # version. If the meta file does not specify it, use GOOGLE_CRASH_ID + # as the product and GOOGLE_CRASH_VERSION_ID as the version. + if [ "${product}" = "undefined" ]; then + product="$(get_key_value /etc/os-release 'GOOGLE_CRASH_ID')" + fi + if [ "${version}" = "undefined" ]; then + version="$(get_key_value /etc/os-release 'GOOGLE_CRASH_VERSION_ID')" + fi + + # If GOOGLE_CRASH_* is undefined, we look for ID and VERSION_ID in + # /etc/os-release. + if [ "${product}" = "undefined" ]; then + product="$(get_key_value /etc/os-release 'ID')" + fi + if [ "${version}" = "undefined" ]; then + version="$(get_key_value /etc/os-release 'VERSION_ID')" + fi + + # If ID or VERSION_ID is undefined, we use the default product name + # and CHROMEOS_RELEASE_VERSION from /etc/lsb-release. + if [ "${product}" = "undefined" ]; then + product="${CHROMEOS_PRODUCT}" + fi + if [ "${version}" = "undefined" ]; then + version="${chromeos_version}" + fi + + local image_type + if is_test_image; then + image_type="test" + elif is_developer_image; then + image_type="dev" + elif [ ${FORCE_OFFICIAL} -ne 0 ]; then + image_type="force-official" + elif is_mock && ! is_mock_successful; then + image_type="mock-fail" + fi + + local boot_mode + if ! crossystem "cros_debug" > /dev/null 2>&1; then + # Sanity-check failed that makes sure crossystem exists. + lecho "Cannot determine boot mode due to error running crossystem command" + boot_mode="missing-crossystem" + elif is_developer_mode; then + boot_mode="dev" + fi + + # Need to strip dashes ourselves as Chrome preserves it in the file + # nowadays. This is also what the Chrome breakpad client does. + guid=$(tr -d '-' < "${CONSENT_ID}") + + local error_type="$(get_key_value "${meta_path}" "error_type")" + [ "${error_type}" = "undefined" ] && error_type= + + lecho "Sending crash:" + if [ "${product}" != "${CHROMEOS_PRODUCT}" ]; then + lecho " Sending crash report on behalf of ${product}" + fi + lecho " Metadata: ${meta_path} (${kind})" + lecho " Payload: ${report_payload}" + lecho " Version: ${version}" + [ -n "${image_type}" ] && lecho " Image type: ${image_type}" + [ -n "${boot_mode}" ] && lecho " Boot mode: ${boot_mode}" + if is_mock; then + lecho " Product: ${product}" + lecho " URL: ${url}" + lecho " Board: ${board}" + lecho " HWClass: ${hwclass}" + lecho " write_payload_size: ${write_payload_size}" + lecho " send_payload_size: ${send_payload_size}" + if [ "${log}" != "undefined" ]; then + lecho " log: @${log}" + fi + if [ "${sig}" != "undefined" ]; then + lecho " sig: ${sig}" + fi + fi + lecho " Exec name: ${exec_name}" + [ -n "${error_type}" ] && lecho " Error type: ${error_type}" + if is_mock; then + if ! is_mock_successful; then + lecho "Mocking unsuccessful send" + return 1 + fi + lecho "Mocking successful send" + return 0 + fi + + # Read in the first proxy, if any, for a given URL. NOTE: The + # double-quotes are necessary due to a bug in dash with the "local" + # builtin command and values that have spaces in them (see + # "https://bugs.launchpad.net/ubuntu/+source/dash/+bug/139097"). + local proxy="`${LIST_PROXIES} -quiet "${url}" | head -1`" + # if a direct connection should be used, unset the proxy variable. + [ "${proxy}" = "direct://" ] && proxy= + local report_id="${TMP_DIR}/report_id" + local curl_stderr="${TMP_DIR}/curl_stderr" + + set +e + curl "${url}" -v ${proxy:+--proxy "$proxy"} \ + --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \ + -F "prod=${product}" \ + -F "ver=${version}" \ + -F "board=${board}" \ + -F "hwclass=${hwclass}" \ + -F "exec_name=${exec_name}" \ + ${image_type:+-F "image_type=${image_type}"} \ + ${boot_mode:+-F "boot_mode=${boot_mode}"} \ + ${error_type:+-F "error_type=${error_type}"} \ + -F "guid=${guid}" \ + -o "${report_id}" \ + "$@" \ + 2>"${curl_stderr}" + curl_result=$? + set -e + + if [ ${curl_result} -eq 0 ]; then + local id="$(cat "${report_id}")" + local product_name + local timestamp="$(date +%s)" + case ${product} in + Chrome_ChromeOS) + if is_official_image; then + product_name="Chrome" + else + product_name="Chromium" + fi + ;; + *) + if is_official_image; then + product_name="ChromeOS" + else + product_name="ChromiumOS" + fi + ;; + esac + printf '%s,%s,%s\n' \ + "${timestamp}" "${id}" "${product_name}" >> "${CHROME_CRASH_LOG}" + lecho "Crash report receipt ID ${id}" + else + lecho "Crash sending failed with exit code ${curl_result}: " \ + "$(cat "${curl_stderr}")" + fi + + rm -f "${report_id}" + + return ${curl_result} +} + +# *.meta files always end with done=1 so we can tell if they are complete. +is_complete_metadata() { + grep -q "done=1" "$1" +} + +# Remove the given report path. +remove_report() { + local base="${1%.*}" + rm -f -- "${base}".* +} + +# Send all crashes from the given directory. This applies even when we're on a +# 3G connection (see crosbug.com/3304 for discussion). +send_crashes() { + local dir="$1" + + if [ ! -d "${dir}" ]; then + return + fi + + # Consider any old files which still have no corresponding meta file + # as orphaned, and remove them. + for old_file in $(${FIND} "${dir}" -mindepth 1 \ + -mmin +$((24 * 60)) -type f); do + if [ ! -e "$(get_base "${old_file}").meta" ]; then + lecho "Removing old orphaned file: ${old_file}." + rm -f -- "${old_file}" + fi + done + + # Look through all metadata (*.meta) files, oldest first. That way, the rate + # limit does not stall old crashes if there's a high amount of new crashes + # coming in. + # For each crash report, first evaluate conditions that might lead to its + # removal to honor user choice and to free disk space as soon as possible, + # then decide whether it should be sent right now or kept for later sending. + for meta_path in $(ls -1tr "${dir}"/*.meta 2>/dev/null); do + lecho "Considering metadata ${meta_path}." + + local kind=$(get_kind "${meta_path}") + if [ "${kind}" != "minidump" ] && \ + [ "${kind}" != "kcrash" ] && \ + [ "${kind}" != "log" ]; then + lecho "Unknown report kind ${kind}. Removing report." + remove_report "${meta_path}" + continue + fi + + if ! is_complete_metadata "${meta_path}"; then + # This report is incomplete, so if it's old, just remove it. + local old_meta=$(${FIND} "${dir}" -mindepth 1 -name \ + $(basename "${meta_path}") -mmin +$((24 * 60)) -type f) + if [ -n "${old_meta}" ]; then + lecho "Removing old incomplete metadata." + remove_report "${meta_path}" + else + lecho "Ignoring recent incomplete metadata." + fi + continue + fi + + if ! is_mock && ! is_official_image; then + lecho "Not an official OS version. Removing crash." + remove_report "${meta_path}" + continue + fi + + # Don't send crash reports from previous sessions while we're in guest mode + # to avoid the impression that crash reporting was enabled, which it isn't. + # (Don't exit right now because subsequent reports may be candidates for + # deletion.) + if ${METRICS_CLIENT} -g; then + lecho "Guest mode has been entered. Delaying crash sending until exited." + continue + fi + + # Remove existing crashes in case user consent has not (yet) been given or + # has been revoked. This must come after the guest mode check because + # ${METRICS_CLIENT} always returns "not consented" in guest mode. + if ! ${METRICS_CLIENT} -c; then + lecho "Crash reporting is disabled. Removing crash." + remove_report "${meta_path}" + continue + fi + + # Skip report if the upload rate is exceeded. (Don't exit right now because + # subsequent reports may be candidates for deletion.) + if ! check_rate; then + lecho "Sending ${meta_path} would exceed rate. Leaving for later." + continue + fi + + # The .meta file should be written *after* all to-be-uploaded files that it + # references. Nevertheless, as a safeguard, a hold-off time of thirty + # seconds after writing the .meta file is ensured. Also, sending of crash + # reports is spread out randomly by up to SECONDS_SEND_SPREAD. Thus, for + # the sleep call the greater of the two delays is used. + local now=$(date +%s) + local holdoff_time=$(($(stat --format=%Y "${meta_path}") + 30 - ${now})) + local spread_time=$(generate_uniform_random "${SECONDS_SEND_SPREAD}") + local sleep_time + if [ ${spread_time} -gt ${holdoff_time} ]; then + sleep_time="${spread_time}" + else + sleep_time="${holdoff_time}" + fi + lecho "Scheduled to send in ${sleep_time}s." + if ! is_mock; then + if ! sleep "${sleep_time}"; then + lecho "Sleep failed" + return 1 + fi + fi + + # Try to upload. + if ! send_crash "${meta_path}"; then + lecho "Problem sending ${meta_path}, not removing." + continue + fi + + # Send was successful, now remove. + lecho "Successfully sent crash ${meta_path} and removing." + remove_report "${meta_path}" + done +} + +usage() { + cat <= Set env |var| to |val| (only some vars) +EOF + exit ${1:-1} +} + +parseargs() { + # Parse the command line arguments. + while [ $# -gt 0 ]; do + case $1 in + -e) + shift + case $1 in + FORCE_OFFICIAL=*|\ + MAX_CRASH_RATE=*|\ + MOCK_DEVELOPER_MODE=*|\ + OVERRIDE_PAUSE_SENDING=*|\ + SECONDS_SEND_SPREAD=*) + export "$1" + ;; + *) + lecho "Unknown var passed to -e: $1" + exit 1 + ;; + esac + ;; + -h) + usage 0 + ;; + *) + lecho "Unknown options: $*" + exit 1 + ;; + esac + shift + done +} + +main() { + trap cleanup EXIT INT TERM + + parseargs "$@" + + if [ -e "${PAUSE_CRASH_SENDING}" ] && \ + [ ${OVERRIDE_PAUSE_SENDING} -eq 0 ]; then + lecho "Exiting early due to ${PAUSE_CRASH_SENDING}." + exit 1 + fi + + if is_test_image; then + lecho "Exiting early due to test image." + exit 1 + fi + + # We don't perform checks on this because we have a master lock with the + # CRASH_SENDER_LOCK file. This pid file is for the system to keep track + # (like with autotests) that we're still running. + echo $$ > "${RUN_FILE}" + + for dependency in "${FIND}" "${METRICS_CLIENT}" \ + "${RESTRICTED_CERTIFICATES_PATH}"; do + if [ ! -x "${dependency}" ]; then + lecho "Fatal: Crash sending disabled: ${dependency} not found." + exit 1 + fi + done + + TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXXXX)" + + # Send system-wide crashes + send_crashes "/var/spool/crash" + + # Send user-specific crashes + local d + for d in /home/chronos/crash /home/chronos/u-*/crash; do + send_crashes "${d}" + done +} + +( +if ! flock -n 9; then + lecho "Already running; quitting." + crash_done + exit 1 +fi +main "$@" +) 9>"${CRASH_SENDER_LOCK}" diff --git a/crash_reporter/crash_sender.conf b/crash_reporter/crash_sender.conf deleted file mode 100644 index 5611c100c..000000000 --- a/crash_reporter/crash_sender.conf +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2014 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can -# be found in the LICENSE file. - -# This file has the format: -# NAME=value - -# Product ID in crash report. -CHROMEOS_PRODUCT=ChromeOS - -# Set this to 1 to allow uploading crash reports for unofficial versions. -FORCE_OFFICIAL=0 - -# Maximum crashes to send per day. -MAX_CRASH_RATE=32 - -# Set this to 1 to pretend to have booted in developer mode. This is used by -# autotests. -MOCK_DEVELOPER_MODE=0 - -# Ignore PAUSE_CRASH_SENDING file if set. -OVERRIDE_PAUSE_SENDING=0 - -# URL to send official build crash reports to. -REPORT_UPLOAD_PROD_URL=https://clients2.google.com/cr/report - -# Maximum time to sleep between sends. -SECONDS_SEND_SPREAD=600 diff --git a/crash_reporter/crash_sender_daemon.cc b/crash_reporter/crash_sender_daemon.cc deleted file mode 100644 index f477bdb59..000000000 --- a/crash_reporter/crash_sender_daemon.cc +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "crash-reporter/crash_sender_daemon.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { -// Parameter to specify a custom config file. -const char kSwitchCustomConfigFile[] = "config"; - -const char kDefaultConfigFile[] = "/etc/crash_sender.conf"; - -const int kTerminationSignals[] = { SIGTERM, SIGINT }; -const int kNumTerminationSignals = arraysize(kTerminationSignals); -} // namespace - - -namespace crash_reporter { -CrashSenderDaemon::CrashSenderDaemon(const base::FilePath& config_file) - : config_file_(config_file) {} - -CrashSenderDaemon::~CrashSenderDaemon() { -} - -void CrashSenderDaemon::Run() { - base::RunLoop loop; - dbus::Bus::Options options; - options.bus_type = dbus::Bus::SYSTEM; - options.disconnected_callback = loop.QuitClosure(); - - scoped_refptr bus = new dbus::Bus(options); - CrashSenderConfiguration config = - CrashSenderService::ParseConfiguration(config_file_); - scoped_ptr impl( - new DbusCrashSenderServiceImpl(config)); - - CHECK(impl->Start(bus)) << "Failed to start crash sender service"; - crash_sender_service_ = impl.Pass(); - - for (size_t i = 0; i < kNumTerminationSignals; ++i) { - async_signal_handler_.RegisterHandler( - kTerminationSignals[i], - base::Bind(&CrashSenderDaemon::Shutdown, base::Unretained(this))); - } - async_signal_handler_.RegisterHandler( - SIGHUP, base::Bind(&CrashSenderDaemon::Restart, base::Unretained(this))); - async_signal_handler_.Init(); - - loop.Run(); - - bus->ShutdownAndBlock(); -} - -bool CrashSenderDaemon::Shutdown(const struct signalfd_siginfo& info) { - loop_.PostTask(FROM_HERE, loop_.QuitClosure()); - // Unregister the signal handler. - return true; -} - -bool CrashSenderDaemon::Restart(const struct signalfd_siginfo& info) { - CrashSenderConfiguration config = - CrashSenderService::ParseConfiguration(config_file_); - crash_sender_service_->Restart(config); - // Keep listening to the signal. - return false; -} - -} // namespace crash_reporter - -int main(int argc, char** argv) { - CommandLine::Init(argc, argv); - CommandLine* args = CommandLine::ForCurrentProcess(); - - // Some libchrome calls need this. - base::AtExitManager at_exit_manager; - - chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogToStderr); - - base::FilePath config_file = - args->GetSwitchValuePath(kSwitchCustomConfigFile); - if (config_file.empty()) { - config_file = base::FilePath(FILE_PATH_LITERAL(kDefaultConfigFile)); - } else { - LOG(INFO) << "Using crash configuration at: " << config_file.value(); - } - - crash_reporter::CrashSenderDaemon daemon(config_file); - daemon.Run(); - return 0; -} diff --git a/crash_reporter/crash_sender_daemon.h b/crash_reporter/crash_sender_daemon.h deleted file mode 100644 index 462a53d27..000000000 --- a/crash_reporter/crash_sender_daemon.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CRASH_REPORTER_CRASH_SENDER_DAEMON_H_ -#define CRASH_REPORTER_CRASH_SENDER_DAEMON_H_ - -#include -#include -#include -#include - -#include "crash-reporter/crash_sender_service.h" - -namespace crash_reporter { - -class CrashSenderDaemon { - public: - // |config_file| specifies the config file for the crash sender. - explicit CrashSenderDaemon(const base::FilePath& config_file); - ~CrashSenderDaemon(); - - // Does all the work. Blocks until the daemon is finished. - void Run(); - - private: - base::MessageLoopForIO loop_; - base::FilePath config_file_; - scoped_ptr crash_sender_service_; - chromeos::AsynchronousSignalHandler async_signal_handler_; - - // Shutdown the sender. - bool Shutdown(const signalfd_siginfo& info); - - // Restart the service, reading the configuration file again. - bool Restart(const signalfd_siginfo& info); - - DISALLOW_COPY_AND_ASSIGN(CrashSenderDaemon); -}; - -} // namespace crash_reporter - -#endif // CRASH_REPORTER_CRASH_SENDER_DAEMON_H_ diff --git a/crash_reporter/crash_sender_service.cc b/crash_reporter/crash_sender_service.cc deleted file mode 100644 index 04fb23cab..000000000 --- a/crash_reporter/crash_sender_service.cc +++ /dev/null @@ -1,853 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "crash-reporter/crash_sender_service.h" - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "vboot/crossystem.h" - -namespace { - -// Default product ID in crash report (used if GOOGLE_CRASH_* is undefined). -const char kDefaultProduct[] = "ChromeOS"; - -// File whose existence implies crash reports may be sent, and whose -// contents includes our machine's anonymized guid. -const char kConsentIdPath[] = "/home/chronos/Consent To Send Stats"; - -// Crash sender lock in case the sender is already running. -const char kCrashSenderLockPath[] = "/var/lock/crash_sender"; - -// Crash sender lock in case the sender is already running for tests. -const char kCrashSenderLockForTestsPath[] = "/var/lock/crash_sender_test"; - -// Path to file that indicates a crash test is currently running. -const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress"; - -// Path to hardware class description. -const char kHWClassPath[] = "/sys/devices/platform/chromeos_acpi/HWID"; - -// Path to file that indicates this is a developer image. -const char kLeaveCorePath[] = "/root/.leave_core"; - -// File whose existence causes crash sending to be delayed (for testing). -// Must be stateful to enable testing kernel crashes. -const char kPauseCrashSendingPath[] = "/var/lib/crash_sender_paused"; - -// Path to a directory of restricted certificates which includes -// a certificate for ${REPORT_UPLOAD_PROD_URL}. -const char kRestrictedCertificatesPath[] = - "/usr/share/chromeos-ca-certificates"; - -// File whose existence implies we're running and not to start again. -const char kRunFilePath[] = "/var/run/crash_sender.pid"; - -// Directory to store timestamp files indicating the uploads in the past 24 -// hours. -const char kTimestampsDirPath[] = "/var/lib/crash_sender"; - -// Chrome's crash report log file. -const char kChromeCrashLogPath[] = "/var/log/chrome/Crash Reports/uploads.log"; - -// File whose existence mocks crash sending. If empty we pretend the crash -// sending was successful, otherwise unsuccessful. -const char kMockCrashSendingPath[] = "/tmp/mock-crash-sending"; - -// Configuration keys. -const char kForceOfficial[] = "FORCE_OFFICIAL"; -const char kMaxCrashRate[] = "MAX_CRASH_RATE"; -const char kMockDeveloperMode[] = "MOCK_DEVELOPER_MODE"; -const char kOverridePauseSending[] = "OVERRIDE_PAUSE_SENDING"; -const char kReportUploadProdUrl[] = "REPORT_UPLOAD_PROD_URL"; -const char kSecondsSendSpread[] = "SECONDS_SEND_SPREAD"; - -// Owns a curl handle. -class ScopedCurl { - public: - explicit ScopedCurl(bool mock) : mock_(mock) { - if (!mock) - curl_ = curl_easy_init(); - } - - ~ScopedCurl() { - if (mock_) - return; - - if (post_) - curl_formfree(post_); - - curl_easy_cleanup(curl_); - } - - CURL* curl() const { return curl_; } - - void AddMultipartContent(std::string key, std::string value) { - LOG(INFO) << key << ": " << value; - if (mock_) - return; - curl_formadd(&post_, &last_, - CURLFORM_COPYNAME, key.c_str(), - CURLFORM_COPYCONTENTS, value.c_str(), - CURLFORM_END); - } - - void AddFile(std::string key, base::FilePath file) { - LOG(INFO) << key << ": " << file.value(); - if (mock_) - return; - curl_formadd(&post_, &last_, - CURLFORM_COPYNAME, key.c_str(), - CURLFORM_FILE, file.value().c_str(), - CURLFORM_END); - } - - CURLcode perform() { - CHECK(!mock_); - curl_easy_setopt(curl_, CURLOPT_HTTPPOST, post_); - return curl_easy_perform(curl_); - } - - private: - bool mock_; - CURL* curl_ = nullptr; - struct curl_httppost* post_ = nullptr; - struct curl_httppost* last_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(ScopedCurl); -}; - -// Comparison function. -bool order_meta_files(const crash_reporter::MetaFile& f1, - const crash_reporter::MetaFile& f2) { - return std::make_tuple(f1.modification_time, f1.path.value()) < - std::make_tuple(f2.modification_time, f2.path.value()); -} - -// Return the list of directories containing crashes. -std::vector GetCrashDirectories() { - std::vector result; - base::FilePath system_wide("/var/spool/crash"); - if (base::DirectoryExists(system_wide)) - result.push_back(system_wide); - - base::FilePath main_user("/home/chronos/crash"); - if (base::DirectoryExists(main_user)) - result.push_back(main_user); - - base::FileEnumerator enumCrashDirectories( - base::FilePath("/home/chronos"), false, base::FileEnumerator::DIRECTORIES, - FILE_PATH_LITERAL("u-*")); - for (base::FilePath dir = enumCrashDirectories.Next(); !dir.empty(); - dir = enumCrashDirectories.Next()) { - base::FilePath crash_dir = dir.Append("crash"); - if (base::DirectoryExists(crash_dir)) - result.push_back(crash_dir); - } - return result; -} - -// Returns all the files in a given directory with the time of last -// modification. -std::vector> GetOrderedFiles( - const base::FilePath& dir) { - std::vector> files; - base::FileEnumerator enumFiles(dir, false, base::FileEnumerator::FILES); - for (base::FilePath file = enumFiles.Next(); !file.empty(); - file = enumFiles.Next()) { - files.push_back( - std::make_pair(enumFiles.GetInfo().GetLastModifiedTime(), file)); - } - return files; -} - -// Parse a file containing key value of the form: -// KEY=VALUE -std::map ParseKeyValueFile( - const base::FilePath& file) { - std::map result; - std::string content; - if (!base::ReadFileToString(file, &content)) { - LOG(WARNING) << "Unable to read key values from: " << file.value(); - return result; - } - base::StringPairs pairs; - base::SplitStringIntoKeyValuePairs(content, '=', '\n', &pairs); - for (base::StringPairs::const_iterator pair = pairs.begin(); - pair != pairs.end(); ++pair) { - if (!pair->first.empty() && pair->first[0] != '#') - result[pair->first] = pair->second; - } - - return result; -} - -// Returns the value associated to |key| in |map|, or |default_value| if |map| -// doesn't contain |key|. -std::string GetValue(const std::map& map, - const std::string& key, const std::string& default_value) { - std::map::const_iterator it = map.find(key); - if (it != map.end()) - return it->second; - - return default_value; -} - -// As |GetValue| for values of type |base::FilePath|. -base::FilePath GetPathValue(const std::map& map, - const std::string& key, - const base::FilePath& default_value) { - std::map::const_iterator it = map.find(key); - if (it != map.end()) - return base::FilePath(it->second); - - return default_value; -} - -// As |GetValue| for values of type |int|. -int GetIntValue(const std::map& map, - const std::string& key, int default_value) { - std::map::const_iterator it = map.find(key); - if (it != map.end()) { - int result; - if (base::StringToInt(it->second, &result)) { - return result; - } - } - return default_value; -} - -// Remove the report for the given |meta_file|. -void RemoveReport(const base::FilePath& meta_file) { - LOG(INFO) << "Removing report: " << meta_file.value(); - base::FilePath directory = meta_file.DirName(); - base::FilePath template_value = meta_file.ReplaceExtension(".*").BaseName(); - - base::FileEnumerator filesToDelete( - directory, false, base::FileEnumerator::FILES, template_value.value()); - for (base::FilePath file = filesToDelete.Next(); !file.empty(); - file = filesToDelete.Next()) { - if (!base::DeleteFile(file, false)) - LOG(WARNING) << "Unable to delete " << file.value(); - } -} - -// Returns the extenstion of the given file, stripping .gz if the file is -// compressed. -std::string GetExtension(const base::FilePath& file) { - std::string extension = file.FinalExtension(); - if (extension == ".gz") - extension = file.RemoveFinalExtension().FinalExtension(); - - if (!extension.empty()) { - DCHECK_EQ(extension[0], '.'); - extension = extension.substr(1); - } - return extension; -} - -// Returns the report kind. -std::string GetKind(const crash_reporter::MetaFile& meta_file) { - base::FilePath payload = - GetPathValue(meta_file.meta_information, "payload", base::FilePath()); - if (payload.value().empty() || !base::PathExists(payload)) { - LOG(WARNING) << "Missing payload on file: " << meta_file.path.value(); - return ""; - } - std::string kind = GetExtension(payload); - if (kind == "dmp") { - return "minidump"; - } - return kind; -} - -// Callback function for curl. It delegates to a callback passed as additional -// data. -size_t CurlWriteData(void* buffer, size_t size, size_t nmemb, void* data) { - base::Callback* callback = - static_cast*>(data); - return callback->Run(buffer, size * nmemb); -} - -size_t AppendDataToString(std::string* data, const void* buffer, size_t size) { - data->append(reinterpret_cast(buffer), size); - return size; -} - - -} // namespace - -namespace crash_reporter { - -CrashSenderService::CrashSenderService(const CrashSenderConfiguration& config) - : config_(config) { - metrics_lib_.Init(); -} - -CrashSenderService::~CrashSenderService() {} - -bool CrashSenderService::Start(ProxyResolver* proxy_resolver) { - proxy_resolver_ = proxy_resolver; - std::map lsb_release_values = - ParseKeyValueFile(base::FilePath("/etc/lsb-release")); - - board_ = lsb_release_values["CHROMEOS_RELEASE_BOARD"]; - if (board_.empty()) { - LOG(ERROR) << "Unable to retrieve board information."; - return false; - } - - channel_ = lsb_release_values["CHROMEOS_RELEASE_TRACK"]; - const char kChannelSuffix[] = "-channel"; - if (EndsWith(channel_, kChannelSuffix, true)) - channel_ = - channel_.substr(0, channel_.size() - arraysize(kChannelSuffix) + 1); - - if (channel_.empty()) { - LOG(ERROR) << "Unable to retrieve channel information."; - return false; - } - - official_ = - (lsb_release_values["CHROMEOS_RELEASE_DESCRIPTION"].find("Official") != - std::string::npos); - - std::map os_release_values = - ParseKeyValueFile(base::FilePath("/etc/os-release")); - - default_product_ = GetValue(os_release_values, "GOOGLE_CRASH_ID", ""); - if (default_product_.empty()) - default_product_ = GetValue(os_release_values, "ID", ""); - - default_version_ = GetValue(os_release_values, "GOOGLE_CRASH_VERSION_ID", ""); - if (default_version_.empty()) - default_version_ = GetValue(os_release_values, "VERSION_ID", ""); - - return ReapplyConfig(config_); -} - -void CrashSenderService::Restart(const CrashSenderConfiguration& config) { - CrashSenderConfiguration old_config = config_; - config_ = config; - if (!ReapplyConfig(config_)) { - LOG(ERROR) << "Restarting failed. Reapplying old configuration."; - config_ = old_config; - CHECK(ReapplyConfig(config_)); - } -} - -CrashSenderConfiguration CrashSenderService::ParseConfiguration( - const base::FilePath& config_file) { - CrashSenderConfiguration result; - std::map key_values = - ParseKeyValueFile(config_file); - - result.force_official = GetIntValue(key_values, kForceOfficial, false); - result.max_crash_rate = GetIntValue(key_values, kMaxCrashRate, 32); - result.mock_developer_mode = - GetIntValue(key_values, kMockDeveloperMode, false); - result.override_pause_sending = - GetIntValue(key_values, kOverridePauseSending, false); - result.report_upload_prod_url = - GetValue(key_values, kReportUploadProdUrl, - "https://clients2.google.com/cr/report"); - result.seconds_send_spread = GetIntValue(key_values, kSecondsSendSpread, 600); - - return result; -} - -bool CrashSenderService::ReapplyConfig(const CrashSenderConfiguration& config) { - bool test_run = IsMock(); - const char* lock_path = - test_run ? kCrashSenderLockForTestsPath : kCrashSenderLockPath; - lock_file_.reset( - new base::File(base::FilePath(lock_path), base::File::FLAG_OPEN_ALWAYS | - base::File::FLAG_READ | - base::File::FLAG_WRITE)); - if (lock_file_->Lock() != base::File::FILE_OK) { - LOG(ERROR) << "Already running; quitting."; - return false; - } - base::FilePath run_file(kRunFilePath); - run_file_deleter_.Reset( - base::Bind(base::IgnoreResult(&base::DeleteFile), run_file, false)); - std::string pid = base::IntToString(getpid()) + "\n"; - base::WriteFile(run_file, pid.data(), pid.length()); - - CollectAllCrashes(); - if (test_run) { - base::MessageLoop::current()->PostTask(FROM_HERE, - base::MessageLoop::QuitClosure()); - LOG(INFO) << "crash_sender done."; - } - return true; -} - -bool CrashSenderService::IsCrashTestInProgress() const { - return base::PathExists(base::FilePath(kCrashTestInProgressPath)); -} - -bool CrashSenderService::IsTestImage() const { - if (IsCrashTestInProgress()) - return false; - - return StartsWithASCII(channel_, "test", true); -} - -bool CrashSenderService::IsMock() const { - return base::PathExists(base::FilePath(kMockCrashSendingPath)); -} - -bool CrashSenderService::IsMockSuccessful() const { - std::string content; - if (base::ReadFileToString(base::FilePath(kMockCrashSendingPath), &content)) - return content.empty(); - - return false; -} - -bool CrashSenderService::IsOfficialImage() const { - return config_.force_official || official_; -} - -bool CrashSenderService::IsDeveloperMode() const { - if (config_.mock_developer_mode) - return true; - - if (IsCrashTestInProgress()) - return false; - - return VbGetSystemPropertyInt("devsw_boot"); -} - -bool CrashSenderService::IsDeveloperImage() const { - // Mirrors crash_collector.cc:CrashCollector::IsDeveloperImage(). - if (IsCrashTestInProgress()) - return false; - - return base::PathExists(base::FilePath(kLeaveCorePath)); -} - -std::string CrashSenderService::GetHardwareClass() const { - std::string content; - if (base::ReadFileToString(base::FilePath(kHWClassPath), &content)) - return content; - - char buffer[VB_MAX_STRING_PROPERTY]; - const char* hwid = - VbGetSystemPropertyString("hwid", buffer, VB_MAX_STRING_PROPERTY); - if (hwid) - return hwid; - - return "undefined"; -} - -std::string CrashSenderService::GetConsentId() const { - std::string content; - if (base::ReadFileToString(base::FilePath(kConsentIdPath), &content)) { - content.erase(std::remove(content.begin(), content.end(), '-'), - content.end()); - return content; - } - - return "undefined"; -} - -void CrashSenderService::CollectCrashes(const base::FilePath& dir) { - std::vector> files = - GetOrderedFiles(dir); - base::Time now = base::Time::Now(); - for (const std::pair& file : files) { - if (file.second.FinalExtension() == ".meta") { - MetaFile info; - info.modification_time = file.first; - info.path = file.second; - info.meta_information = ParseKeyValueFile(info.path); - switch (FilterCrashes(info)) { - case CAN_UPLOAD: - current_crashes_.push_back(info); - break; - case DELETE: - RemoveReport(info.path); - break; - case WAIT: - // Nothing - break; - } - } else if ((now - file.first >= base::TimeDelta::FromDays(1)) && - !base::PathExists(file.second.ReplaceExtension(".meta"))) { - if (base::DeleteFile(file.second, false)) { - LOG(INFO) << "Removing old orphaned file: " << file.second.value(); - } else { - LOG(WARNING) << "Unable to delete: " << file.second.value(); - } - } - } -} - -void CrashSenderService::CollectAllCrashes() { - current_crashes_.clear(); - - std::vector crash_directories = GetCrashDirectories(); - for (const base::FilePath& path : crash_directories) { - CrashSenderService::CollectCrashes(path); - } - std::sort(current_crashes_.begin(), current_crashes_.end(), - &order_meta_files); - - if (current_crashes_.empty()) { - // If no crash is present, wait for an hour. - ScheduleNext(); - return; - } - - PrepareToSendNextCrash(); -} - -CrashSenderService::FileStatus CrashSenderService::FilterCrashes( - const MetaFile& file) { - if (!metrics_lib_.AreMetricsEnabled()) { - LOG(INFO) << "Crash reporting is disabled. Removing crash."; - return DELETE; - } - - if (!IsMock() && !IsOfficialImage()) { - LOG(INFO) << "Not an official OS version. Removing crash."; - return DELETE; - } - - if (GetValue(file.meta_information, "done", "") != "1") { - // This report is incomplete, so if it's old, just remove it - if (base::Time::Now() - file.modification_time >= - base::TimeDelta::FromDays(1)) { - LOG(INFO) << "Removing old incomplete metadata."; - return DELETE; - } else { - LOG(INFO) << "Ignoring recent incomplete metadata."; - return WAIT; - } - } - - std::string report_kind = GetKind(file); - if (report_kind != "minidump" && report_kind != "kcrash" && - report_kind != "log") { - LOG(INFO) << "Unknown report kind " << report_kind << ". Removing report."; - return DELETE; - } - - return CAN_UPLOAD; -} - -bool CrashSenderService::MustThrottle() const { - base::FilePath timestamps_dir(kTimestampsDirPath); - if (!base::CreateDirectoryAndGetError(timestamps_dir, nullptr)) { - LOG(WARNING) << "Unable to create directory: " << timestamps_dir.value(); - return true; - } - - base::Time now = base::Time::Now(); - base::FileEnumerator timestamps(timestamps_dir, false, - base::FileEnumerator::FILES); - int sends_in_24hrs = 0; - for (base::FilePath file = timestamps.Next(); !file.empty(); - file = timestamps.Next()) { - if (now - timestamps.GetInfo().GetLastModifiedTime() >= - base::TimeDelta::FromDays(1)) { - base::DeleteFile(file, false); - } else { - ++sends_in_24hrs; - } - } - LOG(INFO) << "Current send rate: " << sends_in_24hrs << "sends/24hrs"; - if (sends_in_24hrs >= config_.max_crash_rate) { - LOG(INFO) << "Cannot send more crashes: current " << sends_in_24hrs - << "send/24hrs >= max " << config_.max_crash_rate << "send/24hrs"; - return true; - } - base::FilePath tmp_file; - if (!base::CreateTemporaryFileInDir(timestamps_dir, &tmp_file)) { - LOG(WARNING) << "Unable to create a file in " << timestamps_dir.value(); - return true; - } - return false; -} - -void CrashSenderService::PrepareToSendNextCrash() { - // If we cannot send any crashes, wait one hour. - if (!CanSendNextCrash()) { - ScheduleNext(); - return; - } - - // If there is no crash to send, collect crashes and return. - if (current_crashes_.empty()) { - CollectAllCrashes(); - return; - } - - const MetaFile& file = current_crashes_.front(); - base::TimeDelta time_to_wait = - std::max(file.modification_time + base::TimeDelta::FromSeconds(30) - - base::Time::Now(), - base::TimeDelta::FromSeconds( - base::RandInt(1, config_.seconds_send_spread))); - LOG(INFO) << "Scheduled to send " << file.path.value() << " in " - << time_to_wait.InSeconds() << "s."; - - if (IsMock()) { - SendNextCrash(); - } else { - timer_.Start(FROM_HERE, time_to_wait, this, - &CrashSenderService::SendNextCrash); - } -} - -bool CrashSenderService::CanSendNextCrash() { - // Handle pause crash sending - base::FilePath pause_crash_sending(kPauseCrashSendingPath); - if (base::PathExists(pause_crash_sending) && - !config_.override_pause_sending) { - LOG(INFO) << "Not sending crashes due to " << pause_crash_sending.value(); - return false; - } - - // Handle is test image - if (IsTestImage()) { - LOG(INFO) << "Not sending crashes due to test image."; - return false; - } - - // Handle certificate path - base::FilePath restricted_certificates_path(kRestrictedCertificatesPath); - if (!base::DirectoryExists(restricted_certificates_path)) { - LOG(INFO) << "Not sending crashes due to " - << restricted_certificates_path.value() << " missing."; - return false; - } - - // Guest mode. - if (metrics_lib_.IsGuestMode()) { - LOG(INFO) - << "Guest mode has been entered. Delaying crash sending until exited."; - return false; - } - - return true; -} - -void CrashSenderService::SendNextCrash() { - // Ensure the timer will be called again if this is exited early due to - // exceptional conditions. - ScheduleNext(); - - if (!CanSendNextCrash()) - return; - - // Check uploads rate - if (MustThrottle()) { - LOG(INFO) << "Sending a report would exceed rate. Leaving for later."; - return; - } - - const MetaFile file = current_crashes_[0]; - current_crashes_.erase(current_crashes_.begin()); - - // Trying to send a crash. Preparing next crash. - base::ScopedClosureRunner send_next_crash(base::Bind( - &CrashSenderService::PrepareToSendNextCrash, base::Unretained(this))); - // Delete the report whatever the result. - base::ScopedClosureRunner report_delete(base::Bind(&RemoveReport, file.path)); - - ScopedCurl curl(IsMock()); - - LOG(INFO) << "Sending crash:"; - std::string product = GetValue(file.meta_information, "upload_var_prod", ""); - if (product.empty()) - product = default_product_; - - if (product.empty()) - product = kDefaultProduct; - - curl.AddMultipartContent("prod", product); - if (product != kDefaultProduct) - LOG(INFO) << "Sending crash report on behalf of " << product; - - LOG(INFO) << "Metadata: " << file.path.value() << " (" << GetKind(file) - << ")"; - - std::string version = GetValue(file.meta_information, "upload_var_ver", ""); - if (version.empty()) - version = default_version_; - if (version.empty()) - version = GetValue(file.meta_information, "ver", ""); - - curl.AddMultipartContent("ver", version); - curl.AddMultipartContent("board", board_); - curl.AddMultipartContent("hwclass", GetHardwareClass()); - curl.AddMultipartContent( - "exec_name", GetValue(file.meta_information, "exec_name", "undefined")); - - std::string image_type; - if (IsTestImage()) { - image_type = "test"; - } else if (IsDeveloperImage()) { - image_type = "dev"; - } else if (config_.force_official) { - image_type = "force-official"; - } else if (IsMock() && !IsMockSuccessful()) { - image_type = "mock-fail"; - } - if (!image_type.empty()) - curl.AddMultipartContent("image_type", image_type); - - if (VbGetSystemPropertyInt("cros_debug") && IsDeveloperMode()) - curl.AddMultipartContent("boot_mode", "dev"); - - std::string error_type = GetValue(file.meta_information, "error_type", ""); - if (!error_type.empty()) - curl.AddMultipartContent("error_type", error_type); - - curl.AddMultipartContent("guid", GetConsentId()); - curl.AddMultipartContent( - "write_payload_size", - GetValue(file.meta_information, "payload_size", "undefined")); - - base::FilePath payload = - GetPathValue(file.meta_information, "payload", base::FilePath()); - if (!payload.value().empty()) { - int64 file_size; - if (base::GetFileSize(payload, &file_size)) { - curl.AddMultipartContent("send_payload_size", - base::Int64ToString(file_size)); - curl.AddFile("upload_file_" + GetKind(file), payload); - } - } - - std::string signature = GetValue(file.meta_information, "sig", ""); - if (!signature.empty()) { - curl.AddMultipartContent("sig", signature); - curl.AddMultipartContent("sig2", signature); - } - - base::FilePath log = - GetPathValue(file.meta_information, "log", base::FilePath()); - if (base::PathExists(log)) - curl.AddFile("log", log); - - std::string upload_prefix = - GetValue(file.meta_information, "upload_prefix", ""); - - const char kUploadVarPrefix[] = "upload_var_"; - const char kUploadFilePrefix[] = "upload_file_"; - for (const auto& pair : file.meta_information) { - if (StartsWithASCII(pair.first, kUploadVarPrefix, true)) { - curl.AddMultipartContent( - upload_prefix + pair.first.substr(arraysize(kUploadVarPrefix) - 1), - pair.second); - } - if (StartsWithASCII(pair.first, kUploadFilePrefix, true)) { - curl.AddFile( - upload_prefix + pair.first.substr(arraysize(kUploadFilePrefix) - 1), - base::FilePath(pair.second)); - } - } - - if (IsMock()) { - if (IsMockSuccessful()) { - LOG(INFO) << "Mocking successful send"; - } else { - LOG(INFO) << "Mocking unsuccessful send"; - } - return; - } - - curl_easy_setopt(curl.curl(), CURLOPT_URL, - config_.report_upload_prod_url.c_str()); - curl_easy_setopt(curl.curl(), CURLOPT_POST, 1L); - std::vector proxies = proxy_resolver_->GetProxiesForUrl( - config_.report_upload_prod_url, base::TimeDelta::FromSeconds(5)); - if (proxies.size() && proxies[0] != "direct://") - curl_easy_setopt(curl.curl(), CURLOPT_PROXY, proxies[0].c_str()); - - // TODO(qsr) Remove - curl_easy_setopt(curl.curl(), CURLOPT_PROXY, "http://192.168.45.1:8888"); - - std::string received_data = ""; - base::Callback callback = - base::Bind(&AppendDataToString, &received_data); - curl_easy_setopt(curl.curl(), CURLOPT_WRITEFUNCTION, &CurlWriteData); - curl_easy_setopt(curl.curl(), CURLOPT_WRITEDATA, &callback); - - CURLcode success = curl.perform(); - - if (success != 0 || received_data.size() > 20) { - LOG(ERROR) << "Unable to upload crash report. Error code: " << success; - return; - } - - std::string product_name; - if (product == "Chrome_ChromeOS") { - if (IsOfficialImage()) { - product_name = "Chrome"; - } else { - product_name = "Chromium"; - } - } else { - if (IsOfficialImage()) { - product_name = "ChromeOS"; - } else { - product_name = "ChromiumOS"; - } - } - std::string log_string = base::StringPrintf( - "%" PRIu64 ",%s,%s\n", static_cast(base::Time::Now().ToTimeT()), - received_data.c_str(), product_name.c_str()); - if (base::AppendToFile(base::FilePath(kChromeCrashLogPath), log_string.data(), - log_string.size()) == -1) { - LOG(ERROR) << "Unable to update crash log."; - } -} - -void CrashSenderService::ScheduleNext() { - timer_.Start(FROM_HERE, base::TimeDelta::FromHours(1), this, - &CrashSenderService::PrepareToSendNextCrash); -} - -DbusCrashSenderServiceImpl::DbusCrashSenderServiceImpl( - const CrashSenderConfiguration& config) - : CrashSenderService(config) {} - -DbusCrashSenderServiceImpl::~DbusCrashSenderServiceImpl() {} - -bool DbusCrashSenderServiceImpl::Start(dbus::Bus* bus) { - if (!bus || !bus->Connect()) { - LOG(ERROR) << "Failed to connect to DBus"; - return false; - } - - bus_ = bus; - proxy_resolver_.reset(new DBusProxyResolver(bus_)); - proxy_resolver_->Init(); - return CrashSenderService::Start(proxy_resolver_.get()); -} - -} // namespace crash_reporter diff --git a/crash_reporter/crash_sender_service.h b/crash_reporter/crash_sender_service.h deleted file mode 100644 index bc2b35c94..000000000 --- a/crash_reporter/crash_sender_service.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CRASH_REPORTER_CRASH_SENDER_SERVICE_H_ -#define CRASH_REPORTER_CRASH_SENDER_SERVICE_H_ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "crash-reporter/proxy_resolver.h" -#include "metrics/metrics_library.h" - -namespace dbus { -class Bus; -} // namespace dbus - -namespace crash_reporter { - -// The configuration for the crash sender. See |crash_sender.conf| for details. -struct CrashSenderConfiguration { - bool force_official; - int max_crash_rate; - bool mock_developer_mode; - bool override_pause_sending; - std::string report_upload_prod_url; - int seconds_send_spread; -}; - -// The information about a crash report, which is obtained from the associated -// meta file. -struct MetaFile { - base::Time modification_time; - base::FilePath path; - std::map meta_information; -}; - -class CrashSenderService { - public: - explicit CrashSenderService(const CrashSenderConfiguration& config); - virtual ~CrashSenderService(); - - bool Start(ProxyResolver* proxy_resolver); - - void Restart(const CrashSenderConfiguration& config); - - static CrashSenderConfiguration ParseConfiguration( - const base::FilePath& config_file); - - private: - enum FileStatus { - CAN_UPLOAD, - WAIT, - DELETE, - }; - - bool ReapplyConfig(const CrashSenderConfiguration& config); - bool IsCrashTestInProgress() const; - bool IsTestImage() const; - bool IsMock() const; - bool IsMockSuccessful() const; - bool IsOfficialImage() const; - bool IsDeveloperMode() const; - bool IsDeveloperImage() const; - std::string GetHardwareClass() const; - std::string GetConsentId() const; - void CollectCrashes(const base::FilePath& dir); - void CollectAllCrashes(); - FileStatus FilterCrashes(const MetaFile& file); - bool MustThrottle() const; - void PrepareToSendNextCrash(); - bool CanSendNextCrash(); - void SendNextCrash(); - void ScheduleNext(); - - ProxyResolver* proxy_resolver_ = nullptr; - CrashSenderConfiguration config_; - MetricsLibrary metrics_lib_; - base::OneShotTimer timer_; - base::ScopedClosureRunner run_file_deleter_; - scoped_ptr lock_file_; - std::string channel_; - std::string board_; - std::string default_product_; - std::string default_version_; - bool official_ = false; - std::vector current_crashes_; - - DISALLOW_COPY_AND_ASSIGN(CrashSenderService); -}; - -class DbusCrashSenderServiceImpl : public CrashSenderService { - public: - explicit DbusCrashSenderServiceImpl(const CrashSenderConfiguration& config); - virtual ~DbusCrashSenderServiceImpl(); - - bool Start(dbus::Bus* bus); - - private: - dbus::Bus* bus_ = nullptr; - scoped_ptr proxy_resolver_; - - DISALLOW_COPY_AND_ASSIGN(DbusCrashSenderServiceImpl); -}; -} // namespace crash_reporter - -#endif // CRASH_REPORTER_CRASH_SENDER_SERVICE_H_ diff --git a/crash_reporter/init/crash-sender.conf b/crash_reporter/init/crash-sender.conf index 11b7e4fd1..892186f91 100644 --- a/crash_reporter/init/crash-sender.conf +++ b/crash_reporter/init/crash-sender.conf @@ -1,12 +1,11 @@ -# Copyright 2014 The Chromium OS Authors. All rights reserved. +# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -description "Runs a daemon which send collected crash reports." -author "chromium-os-dev@chromium.org" +description "Run the crash sender periodically" +author "chromium-os-dev@chromium.org" -start on started system-services +start on starting system-services stop on stopping system-services -respawn -exec crash_sender +exec periodic_scheduler 3600 14400 crash_sender /sbin/crash_sender diff --git a/crash_reporter/libproxies.cc b/crash_reporter/libproxies.cc deleted file mode 100644 index cb77295ab..000000000 --- a/crash_reporter/libproxies.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "crash-reporter/libproxies.h" - -#include - -#include -#include -#include - -namespace crash_reporter { - -const char kLibCrosProxyResolveSignalInterface[] = - "org.chromium.CrashReporterLibcrosProxyResolvedInterface"; -const char kLibCrosProxyResolveName[] = "ProxyResolved"; -const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface"; -const char kLibCrosServiceName[] = "org.chromium.LibCrosService"; -const char kLibCrosServicePath[] = "/org/chromium/LibCrosService"; -const char kLibCrosServiceResolveNetworkProxyMethodName[] = - "ResolveNetworkProxy"; -const char kNoProxy[] = "direct://"; - -std::vector ParseProxyString(const std::string& input) { - std::vector ret; - // Some of this code taken from - // https://chromium.googlesource.com/chromium/chromium/+/master/net/proxy - for (const std::string& token : chromeos::string_utils::Split(input, ';')) { - auto space = - std::find_if(token.begin(), token.end(), IsAsciiWhitespace); - std::string scheme(token.begin(), space); - base::StringToLowerASCII(&scheme); - // Chrome uses "socks" to mean socks4 and "proxy" to mean http. - if (scheme == "socks") { - scheme += "4"; - } else if (scheme == "proxy") { - scheme = "http"; - } else if (scheme != "https" && scheme != "socks4" && scheme != "socks5" && - scheme != "direct") { - continue; // Invalid proxy scheme - } - - std::string host_and_port = std::string(space, token.end()); - base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port); - if (scheme != "direct" && host_and_port.empty()) - continue; // Must supply host/port when non-direct proxy used. - - ret.push_back(scheme + "://" + host_and_port); - } - if (ret.empty() || ret.back() != kNoProxy) - ret.push_back(kNoProxy); - - return ret; -} - -} // namespace crash_reporter diff --git a/crash_reporter/libproxies.h b/crash_reporter/libproxies.h deleted file mode 100644 index ed32f34d0..000000000 --- a/crash_reporter/libproxies.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CRASH_REPORTER_LIBPROXIES_H_ -#define CRASH_REPORTER_LIBPROXIES_H_ - -#include -#include - -namespace crash_reporter { - -extern const char kLibCrosProxyResolveSignalInterface[]; -extern const char kLibCrosProxyResolveName[]; -extern const char kLibCrosServiceInterface[]; -extern const char kLibCrosServiceName[]; -extern const char kLibCrosServicePath[]; -extern const char kLibCrosServiceResolveNetworkProxyMethodName[]; -extern const char kNoProxy[]; - -// Copied from src/update_engine/chrome_browser_proxy_resolver.cc -// Parses the browser's answer for resolved proxies. It returns a -// list of strings, each of which is a resolved proxy. -std::vector ParseProxyString(const std::string& input); - -} // namespace crash_reporter - -#endif // CRASH_REPORTER_LIBPROXIES_H_ diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc new file mode 100644 index 000000000..282c6ae02 --- /dev/null +++ b/crash_reporter/list_proxies.cc @@ -0,0 +1,257 @@ +// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include // for isatty() + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +const char kLibCrosProxyResolveSignalInterface[] = + "org.chromium.CrashReporterLibcrosProxyResolvedInterface"; +const char kLibCrosProxyResolveName[] = "ProxyResolved"; +const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface"; +const char kLibCrosServiceName[] = "org.chromium.LibCrosService"; +const char kLibCrosServicePath[] = "/org/chromium/LibCrosService"; +const char kLibCrosServiceResolveNetworkProxyMethodName[] = + "ResolveNetworkProxy"; +const char kNoProxy[] = "direct://"; + +namespace switches { + +const unsigned kTimeoutDefault = 5; + +const char kHelp[] = "help"; +const char kQuiet[] = "quiet"; +const char kTimeout[] = "timeout"; +const char kVerbose[] = "verbose"; +// Help message to show when the --help command line switch is specified. +const char kHelpMessage[] = + "Chromium OS Crash helper: proxy lister\n" + "\n" + "Available Switches:\n" + " --quiet Only print the proxies\n" + " --verbose Print additional messages even when not run from a TTY\n" + " --timeout=N Set timeout for browser resolving proxies (default is 5)\n" + " --help Show this help.\n"; + +} // namespace switches + +static const char *GetGErrorMessage(const GError *error) { + if (!error) + return "Unknown error."; + return error->message; +} + +// Copied from src/update_engine/chrome_browser_proxy_resolver.cc +// Parses the browser's answer for resolved proxies. It returns a +// list of strings, each of which is a resolved proxy. +std::deque ParseProxyString(const std::string &input) { + std::deque ret; + // Some of this code taken from + // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and + // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc + base::StringTokenizer entry_tok(input, ";"); + while (entry_tok.GetNext()) { + std::string token = entry_tok.token(); + base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token); + + // Start by finding the first space (if any). + std::string::iterator space; + for (space = token.begin(); space != token.end(); ++space) { + if (IsAsciiWhitespace(*space)) { + break; + } + } + + std::string scheme = std::string(token.begin(), space); + base::StringToLowerASCII(&scheme); + // Chrome uses "socks" to mean socks4 and "proxy" to mean http. + if (scheme == "socks") + scheme += "4"; + else if (scheme == "proxy") + scheme = "http"; + else if (scheme != "https" && + scheme != "socks4" && + scheme != "socks5" && + scheme != "direct") + continue; // Invalid proxy scheme + + std::string host_and_port = std::string(space, token.end()); + base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port); + if (scheme != "direct" && host_and_port.empty()) + continue; // Must supply host/port when non-direct proxy used. + ret.push_back(scheme + "://" + host_and_port); + } + if (ret.empty() || *ret.rbegin() != kNoProxy) + ret.push_back(kNoProxy); + return ret; +} + +// Define a signal-watcher class to handle the D-Bus signal sent to us when +// the browser answers our request to resolve proxies. +class BrowserProxyResolvedSignalWatcher : public chromeos::dbus::SignalWatcher { + public: + explicit BrowserProxyResolvedSignalWatcher(GMainLoop *main_loop, + std::deque *proxies) + : main_loop_(main_loop), proxies_(proxies) { } + + void OnSignal(DBusMessage *message) override { + // Get args + char *source_url = NULL; + char *proxy_list = NULL; + char *error = NULL; + DBusError arg_error; + dbus_error_init(&arg_error); + if (!dbus_message_get_args(message, &arg_error, + DBUS_TYPE_STRING, &source_url, + DBUS_TYPE_STRING, &proxy_list, + DBUS_TYPE_STRING, &error, + DBUS_TYPE_INVALID)) { + LOG(ERROR) << "Error reading D-Bus signal"; + return; + } + if (!source_url || !proxy_list) { + LOG(ERROR) << "Error getting url, proxy list from D-Bus signal"; + return; + } + + const std::deque &proxies = ParseProxyString(proxy_list); + for (std::deque::const_iterator it = proxies.begin(); + it != proxies.end(); ++it) { + LOG(INFO) << "Found proxy via browser signal: " << (*it).c_str(); + proxies_->push_back(*it); + } + + g_main_loop_quit(main_loop_); + } + + private: + GMainLoop *main_loop_; + std::deque *proxies_; +}; + +static gboolean HandleBrowserTimeout(void *data) { + GMainLoop *main_loop = reinterpret_cast(data); + LOG(ERROR) << "Timeout while waiting for browser to resolve proxy"; + g_main_loop_quit(main_loop); + return false; // only call once +} + +static bool ShowBrowserProxies(std::string url, unsigned timeout) { + GMainLoop *main_loop = g_main_loop_new(NULL, false); + + chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); + if (!dbus.HasConnection()) { + LOG(ERROR) << "Error connecting to system D-Bus"; + return false; + } + chromeos::dbus::Proxy browser_proxy(dbus, + kLibCrosServiceName, + kLibCrosServicePath, + kLibCrosServiceInterface); + if (!browser_proxy) { + LOG(ERROR) << "Error creating D-Bus proxy to interface " + << "'" << kLibCrosServiceName << "'"; + return false; + } + + // Watch for a proxy-resolved signal sent to us + std::deque proxies; + BrowserProxyResolvedSignalWatcher proxy_resolver(main_loop, &proxies); + proxy_resolver.StartMonitoring(kLibCrosProxyResolveSignalInterface, + kLibCrosProxyResolveName); + + // Request the proxies for our URL. The answer is sent to us via a + // proxy-resolved signal. + GError *gerror = NULL; + if (!dbus_g_proxy_call(browser_proxy.gproxy(), + kLibCrosServiceResolveNetworkProxyMethodName, + &gerror, + G_TYPE_STRING, url.c_str(), + G_TYPE_STRING, kLibCrosProxyResolveSignalInterface, + G_TYPE_STRING, kLibCrosProxyResolveName, + G_TYPE_INVALID, G_TYPE_INVALID)) { + LOG(ERROR) << "Error performing D-Bus proxy call " + << "'" << kLibCrosServiceResolveNetworkProxyMethodName << "'" + << ": " << GetGErrorMessage(gerror); + return false; + } + + // Setup a timeout in case the browser doesn't respond with our signal + g_timeout_add_seconds(timeout, &HandleBrowserTimeout, main_loop); + + // Loop until we either get the proxy-resolved signal, or until the + // timeout is reached. + g_main_loop_run(main_loop); + + // If there are no proxies, then we failed to get the proxy-resolved + // signal (e.g. timeout was reached). + if (proxies.empty()) + return false; + + for (std::deque::const_iterator it = proxies.begin(); + it != proxies.end(); ++it) { + printf("%s\n", (*it).c_str()); + } + return true; +} + +int main(int argc, char *argv[]) { + CommandLine::Init(argc, argv); + CommandLine* cl = CommandLine::ForCurrentProcess(); + + if (cl->HasSwitch(switches::kHelp)) { + LOG(INFO) << switches::kHelpMessage; + return 0; + } + + bool quiet = cl->HasSwitch(switches::kQuiet); + bool verbose = cl->HasSwitch(switches::kVerbose); + + unsigned timeout = switches::kTimeoutDefault; + std::string str_timeout = cl->GetSwitchValueASCII(switches::kTimeout); + if (!str_timeout.empty() && !base::StringToUint(str_timeout, &timeout)) { + LOG(ERROR) << "Invalid timeout value: " << str_timeout; + return 1; + } + + // Default to logging to syslog. + int init_flags = chromeos::kLogToSyslog; + // Log to stderr if a TTY (and "-quiet" wasn't passed), or if "-verbose" + // was passed. + + if ((!quiet && isatty(STDERR_FILENO)) || verbose) + init_flags |= chromeos::kLogToStderr; + chromeos::InitLog(init_flags); + + ::g_type_init(); + + std::string url; + CommandLine::StringVector urls = cl->GetArgs(); + if (!urls.empty()) { + url = urls[0]; + LOG(INFO) << "Resolving proxies for URL: " << url; + } else { + LOG(INFO) << "Resolving proxies without URL"; + } + + if (!ShowBrowserProxies(url, timeout)) { + LOG(ERROR) << "Error resolving proxies via the browser"; + LOG(INFO) << "Assuming direct proxy"; + printf("%s\n", kNoProxy); + } + + return 0; +} diff --git a/crash_reporter/proxy_resolver.cc b/crash_reporter/proxy_resolver.cc deleted file mode 100644 index a4cc73247..000000000 --- a/crash_reporter/proxy_resolver.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "crash-reporter/proxy_resolver.h" - -#include - -#include "crash-reporter/libproxies.h" - -namespace crash_reporter { - -DBusProxyResolver::DBusProxyResolver(dbus::Bus* bus) : bus_(bus) {} - -void DBusProxyResolver::Init() { - lib_cros_service_proxy_ = bus_->GetObjectProxy( - kLibCrosServiceName, dbus::ObjectPath(kLibCrosServicePath)); - if (!lib_cros_service_proxy_) { - LOG(WARNING) << "Unable to connect to LibCrosService."; - } -} - -std::vector DBusProxyResolver::GetProxiesForUrl( - const std::string& url, const base::TimeDelta& timeout) { - if (!lib_cros_service_proxy_) return {kNoProxy}; - - auto response = chromeos::dbus_utils::CallMethodAndBlockWithTimeout( - timeout.InMilliseconds(), lib_cros_service_proxy_, - kLibCrosProxyResolveSignalInterface, - kLibCrosServiceResolveNetworkProxyMethodName, url); - if (response) { - std::string returned_message; - if (chromeos::dbus_utils::ExtractMethodCallResults(response.get(), nullptr, - &returned_message)) { - return ParseProxyString(returned_message); - } - } - return {kNoProxy}; -} - -} // namespace crash_reporter diff --git a/crash_reporter/proxy_resolver.h b/crash_reporter/proxy_resolver.h deleted file mode 100644 index a6d1989ca..000000000 --- a/crash_reporter/proxy_resolver.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CRASH_REPORTER_PROXY_RESOLVER_H_ -#define CRASH_REPORTER_PROXY_RESOLVER_H_ - -#include -#include - -#include -#include - -namespace base { -class TimeDelta; -} // namespace base - -namespace crash_reporter { - -class ProxyResolver { - public: - virtual ~ProxyResolver() {} - - virtual std::vector GetProxiesForUrl( - const std::string& url, const base::TimeDelta& timeout) = 0; -}; - -class DBusProxyResolver : public ProxyResolver { - public: - explicit DBusProxyResolver(dbus::Bus* bus); - ~DBusProxyResolver() override = default; - - void Init(); - - std::vector GetProxiesForUrl( - const std::string& url, const base::TimeDelta& timeout) override; - - private: - scoped_refptr bus_; - scoped_refptr lib_cros_service_proxy_; -}; - -} // namespace crash_reporter - -#endif // CRASH_REPORTER_PROXY_RESOLVER_H_ From 55cb40aa95c64e2d7c6385c7e86a598968d5b841 Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Tue, 2 Sep 2014 17:06:24 -0700 Subject: [PATCH 200/225] crash-reporter: Attach Chrome logs to crash reports. When Chrome crashes, gather the last 20 lines of each of the latest two log files in /var/log/chrome and /home/chronos/user/log and include them in the archive file that's attached to crash reports. BUG=chromium:405732 TEST=triggered crashes at the login screen and after logging in and verified that logs were included in crash reports: bdf766f0d6d1e066, a9d410e1a86f996b Change-Id: I09e3cee23af108dc216d64aae85d78751d5649d4 Reviewed-on: https://chromium-review.googlesource.com/216427 Reviewed-by: Ben Chan Tested-by: Daniel Erat Commit-Queue: Daniel Erat --- crash_reporter/chrome_collector.cc | 104 ++++++++++++++---------- crash_reporter/chrome_collector.h | 6 ++ crash_reporter/crash_reporter_logs.conf | 4 + 3 files changed, 70 insertions(+), 44 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 438ecbc80..21cc852a0 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -42,8 +42,25 @@ bool GetDelimitedString(const std::string &str, char ch, size_t offset, return true; } -bool GetDriErrorState(const chromeos::dbus::Proxy &proxy, - const FilePath &error_state_path) { +// Gets the GPU's error state from debugd and writes it to |error_state_path|. +// Returns true on success. +bool GetDriErrorState(const FilePath &error_state_path) { + chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); + if (!dbus.HasConnection()) { + LOG(ERROR) << "Error connecting to system D-Bus"; + return false; + } + + chromeos::dbus::Proxy proxy(dbus, + debugd::kDebugdServiceName, + debugd::kDebugdServicePath, + debugd::kDebugdInterface); + if (!proxy) { + LOG(ERROR) << "Error creating D-Bus proxy to interface " + << "'" << debugd::kDebugdServiceName << "'"; + return false; + } + chromeos::glib::ScopedError error; gchar *error_state = NULL; if (!dbus_g_proxy_call(proxy.gproxy(), debugd::kGetLog, @@ -88,46 +105,6 @@ bool GetDriErrorState(const chromeos::dbus::Proxy &proxy, return true; } -bool GetAdditionalLogs(const FilePath &log_path) { - chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); - if (!dbus.HasConnection()) { - LOG(ERROR) << "Error connecting to system D-Bus"; - return false; - } - - chromeos::dbus::Proxy proxy(dbus, - debugd::kDebugdServiceName, - debugd::kDebugdServicePath, - debugd::kDebugdInterface); - if (!proxy) { - LOG(ERROR) << "Error creating D-Bus proxy to interface " - << "'" << debugd::kDebugdServiceName << "'"; - return false; - } - - FilePath error_state_path = - log_path.DirName().Append("i915_error_state.log.xz"); - if (!GetDriErrorState(proxy, error_state_path)) - return false; - - chromeos::ProcessImpl tar_process; - tar_process.AddArg(kTarPath); - tar_process.AddArg("cfJ"); - tar_process.AddArg(log_path.value()); - tar_process.AddStringOption("-C", log_path.DirName().value()); - tar_process.AddArg(error_state_path.BaseName().value()); - int res = tar_process.Run(); - - base::DeleteFile(error_state_path, false); - - if (res || !base::PathExists(log_path)) { - LOG(ERROR) << "Could not tar file " << log_path.value(); - return false; - } - - return true; -} - } // namespace @@ -158,7 +135,7 @@ bool ChromeCollector::HandleCrash(const FilePath &file_path, std::string dump_basename = FormatDumpBasename(exe_name, time(NULL), pid); FilePath meta_path = GetCrashPath(dir, dump_basename, "meta"); FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp"); - FilePath log_path = GetCrashPath(dir, dump_basename, "log.tar.xz"); + FilePath log_path = GetCrashPath(dir, dump_basename, "log.tar.gz"); std::string data; if (!base::ReadFileToString(file_path, &data)) { @@ -171,7 +148,7 @@ bool ChromeCollector::HandleCrash(const FilePath &file_path, return false; } - if (GetAdditionalLogs(log_path)) { + if (GetAdditionalLogs(log_path, exe_name)) { int64_t minidump_size = 0; int64_t log_size = 0; if (base::GetFileSize(minidump_path, &minidump_size) && @@ -295,5 +272,44 @@ bool ChromeCollector::ParseCrashLog(const std::string &data, return at == data.size(); } +bool ChromeCollector::GetAdditionalLogs(const FilePath &log_path, + const std::string &exe_name) { + std::vector logs_to_compress; + + // Run the command specified by the config file to gather logs. + const FilePath gathered_logs_path = + log_path.DirName().Append("gathered_logs.txt"); + if (GetLogContents(log_config_path_, exe_name, gathered_logs_path)) + logs_to_compress.push_back(gathered_logs_path); + + // Now get the GPU state from debugd. + const FilePath dri_error_state_path = + log_path.DirName().Append("i915_error_state.log.xz"); + if (GetDriErrorState(dri_error_state_path)) + logs_to_compress.push_back(dri_error_state_path); + + if (logs_to_compress.empty()) + return false; + + chromeos::ProcessImpl tar_process; + tar_process.AddArg(kTarPath); + tar_process.AddArg("cfz"); + tar_process.AddArg(log_path.value()); + tar_process.AddStringOption("-C", log_path.DirName().value()); + for (size_t i = 0; i < logs_to_compress.size(); ++i) + tar_process.AddArg(logs_to_compress[i].BaseName().value()); + + int res = tar_process.Run(); + + for (size_t i = 0; i < logs_to_compress.size(); ++i) + base::DeleteFile(logs_to_compress[i], false); + + if (res || !base::PathExists(log_path)) { + LOG(ERROR) << "Could not create tar archive " << log_path.value(); + return false; + } + return true; +} + // static const char ChromeCollector::kSuccessMagic[] = "_sys_cr_finished"; diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h index d8602b93b..67b5aab29 100644 --- a/crash_reporter/chrome_collector.h +++ b/crash_reporter/chrome_collector.h @@ -48,6 +48,12 @@ class ChromeCollector : public CrashCollector { const base::FilePath &minidump, const std::string &basename); + // Gathers additional logs for |exe_name| and compresses them into a .tar.gz + // archive at |log_path|. Returns true if the log archive was created + // successfully. + bool GetAdditionalLogs(const base::FilePath &log_path, + const std::string &exe_name); + FILE *output_file_ptr_; DISALLOW_COPY_AND_ASSIGN(ChromeCollector); diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index e4ad207dc..db0c11647 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -20,6 +20,10 @@ update_engine:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/ # so it is handled in the same way as update_engine. cros_installer:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/update_engine/.) | tail -c 50000 +# Dump the last 20 lines of the last two files in Chrome's system and user log +# directories. +chrome:for f in $(ls -1rt /var/log/chrome/chrome_[0-9]* | tail -2) $(ls -1rt /home/chronos/u-*/log/chrome_[0-9]* | tail -2); do echo "===$f (tail)==="; tail -20 $f; echo EOF; done + # The following rule is used for generating additional diagnostics when # collection of user crashes fails. This output should not be too large # as it is stored in memory. The output format specified for 'ps' is the From 732bd3bb856fe07d15cbbc1770a21dfe6b30ef9a Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Fri, 12 Sep 2014 18:16:02 -0700 Subject: [PATCH 201/225] Revert "crash-reporter: Attach Chrome logs to crash reports." I'm reverting this until I get a privacy review. BUG=chromium:405732 TEST=none Change-Id: Ib3af4752533f76c340d9b853be985bfc774104da Reviewed-on: https://chromium-review.googlesource.com/218010 Reviewed-by: Daniel Erat Commit-Queue: Daniel Erat Tested-by: Daniel Erat --- crash_reporter/chrome_collector.cc | 104 ++++++++++-------------- crash_reporter/chrome_collector.h | 6 -- crash_reporter/crash_reporter_logs.conf | 4 - 3 files changed, 44 insertions(+), 70 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 21cc852a0..438ecbc80 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -42,25 +42,8 @@ bool GetDelimitedString(const std::string &str, char ch, size_t offset, return true; } -// Gets the GPU's error state from debugd and writes it to |error_state_path|. -// Returns true on success. -bool GetDriErrorState(const FilePath &error_state_path) { - chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); - if (!dbus.HasConnection()) { - LOG(ERROR) << "Error connecting to system D-Bus"; - return false; - } - - chromeos::dbus::Proxy proxy(dbus, - debugd::kDebugdServiceName, - debugd::kDebugdServicePath, - debugd::kDebugdInterface); - if (!proxy) { - LOG(ERROR) << "Error creating D-Bus proxy to interface " - << "'" << debugd::kDebugdServiceName << "'"; - return false; - } - +bool GetDriErrorState(const chromeos::dbus::Proxy &proxy, + const FilePath &error_state_path) { chromeos::glib::ScopedError error; gchar *error_state = NULL; if (!dbus_g_proxy_call(proxy.gproxy(), debugd::kGetLog, @@ -105,6 +88,46 @@ bool GetDriErrorState(const FilePath &error_state_path) { return true; } +bool GetAdditionalLogs(const FilePath &log_path) { + chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); + if (!dbus.HasConnection()) { + LOG(ERROR) << "Error connecting to system D-Bus"; + return false; + } + + chromeos::dbus::Proxy proxy(dbus, + debugd::kDebugdServiceName, + debugd::kDebugdServicePath, + debugd::kDebugdInterface); + if (!proxy) { + LOG(ERROR) << "Error creating D-Bus proxy to interface " + << "'" << debugd::kDebugdServiceName << "'"; + return false; + } + + FilePath error_state_path = + log_path.DirName().Append("i915_error_state.log.xz"); + if (!GetDriErrorState(proxy, error_state_path)) + return false; + + chromeos::ProcessImpl tar_process; + tar_process.AddArg(kTarPath); + tar_process.AddArg("cfJ"); + tar_process.AddArg(log_path.value()); + tar_process.AddStringOption("-C", log_path.DirName().value()); + tar_process.AddArg(error_state_path.BaseName().value()); + int res = tar_process.Run(); + + base::DeleteFile(error_state_path, false); + + if (res || !base::PathExists(log_path)) { + LOG(ERROR) << "Could not tar file " << log_path.value(); + return false; + } + + return true; +} + } // namespace @@ -135,7 +158,7 @@ bool ChromeCollector::HandleCrash(const FilePath &file_path, std::string dump_basename = FormatDumpBasename(exe_name, time(NULL), pid); FilePath meta_path = GetCrashPath(dir, dump_basename, "meta"); FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp"); - FilePath log_path = GetCrashPath(dir, dump_basename, "log.tar.gz"); + FilePath log_path = GetCrashPath(dir, dump_basename, "log.tar.xz"); std::string data; if (!base::ReadFileToString(file_path, &data)) { @@ -148,7 +171,7 @@ bool ChromeCollector::HandleCrash(const FilePath &file_path, return false; } - if (GetAdditionalLogs(log_path, exe_name)) { + if (GetAdditionalLogs(log_path)) { int64_t minidump_size = 0; int64_t log_size = 0; if (base::GetFileSize(minidump_path, &minidump_size) && @@ -272,44 +295,5 @@ bool ChromeCollector::ParseCrashLog(const std::string &data, return at == data.size(); } -bool ChromeCollector::GetAdditionalLogs(const FilePath &log_path, - const std::string &exe_name) { - std::vector logs_to_compress; - - // Run the command specified by the config file to gather logs. - const FilePath gathered_logs_path = - log_path.DirName().Append("gathered_logs.txt"); - if (GetLogContents(log_config_path_, exe_name, gathered_logs_path)) - logs_to_compress.push_back(gathered_logs_path); - - // Now get the GPU state from debugd. - const FilePath dri_error_state_path = - log_path.DirName().Append("i915_error_state.log.xz"); - if (GetDriErrorState(dri_error_state_path)) - logs_to_compress.push_back(dri_error_state_path); - - if (logs_to_compress.empty()) - return false; - - chromeos::ProcessImpl tar_process; - tar_process.AddArg(kTarPath); - tar_process.AddArg("cfz"); - tar_process.AddArg(log_path.value()); - tar_process.AddStringOption("-C", log_path.DirName().value()); - for (size_t i = 0; i < logs_to_compress.size(); ++i) - tar_process.AddArg(logs_to_compress[i].BaseName().value()); - - int res = tar_process.Run(); - - for (size_t i = 0; i < logs_to_compress.size(); ++i) - base::DeleteFile(logs_to_compress[i], false); - - if (res || !base::PathExists(log_path)) { - LOG(ERROR) << "Could not create tar archive " << log_path.value(); - return false; - } - return true; -} - // static const char ChromeCollector::kSuccessMagic[] = "_sys_cr_finished"; diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h index 67b5aab29..d8602b93b 100644 --- a/crash_reporter/chrome_collector.h +++ b/crash_reporter/chrome_collector.h @@ -48,12 +48,6 @@ class ChromeCollector : public CrashCollector { const base::FilePath &minidump, const std::string &basename); - // Gathers additional logs for |exe_name| and compresses them into a .tar.gz - // archive at |log_path|. Returns true if the log archive was created - // successfully. - bool GetAdditionalLogs(const base::FilePath &log_path, - const std::string &exe_name); - FILE *output_file_ptr_; DISALLOW_COPY_AND_ASSIGN(ChromeCollector); diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index db0c11647..e4ad207dc 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -20,10 +20,6 @@ update_engine:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/ # so it is handled in the same way as update_engine. cros_installer:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/update_engine/.) | tail -c 50000 -# Dump the last 20 lines of the last two files in Chrome's system and user log -# directories. -chrome:for f in $(ls -1rt /var/log/chrome/chrome_[0-9]* | tail -2) $(ls -1rt /home/chronos/u-*/log/chrome_[0-9]* | tail -2); do echo "===$f (tail)==="; tail -20 $f; echo EOF; done - # The following rule is used for generating additional diagnostics when # collection of user crashes fails. This output should not be too large # as it is stored in memory. The output format specified for 'ps' is the From 262d798b380a59ef691ede4c6a32aa71ff191295 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Thu, 18 Sep 2014 08:05:20 -0700 Subject: [PATCH 202/225] crash-reporter: Replace NULL with nullptr. BUG=None TEST=`FEATURES=test emerge-$BOARD crash-reporter` Change-Id: If0804613ee0385752d01f7bbe01902ffef53bd94 Reviewed-on: https://chromium-review.googlesource.com/218870 Reviewed-by: Alex Vakulenko Commit-Queue: Ben Chan Tested-by: Ben Chan --- crash_reporter/chrome_collector.cc | 6 +++--- crash_reporter/crash_collector.cc | 18 +++++++++--------- crash_reporter/crash_collector.h | 2 +- crash_reporter/crash_reporter.cc | 2 +- crash_reporter/kernel_collector.cc | 7 ++++--- crash_reporter/kernel_warning_collector.cc | 5 +++-- crash_reporter/list_proxies.cc | 10 +++++----- crash_reporter/udev_collector.cc | 4 ++-- crash_reporter/user_collector.cc | 8 ++++---- crash_reporter/user_collector_test.cc | 4 ++-- 10 files changed, 34 insertions(+), 32 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 438ecbc80..b7fa7ca2e 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -45,7 +45,7 @@ bool GetDelimitedString(const std::string &str, char ch, size_t offset, bool GetDriErrorState(const chromeos::dbus::Proxy &proxy, const FilePath &error_state_path) { chromeos::glib::ScopedError error; - gchar *error_state = NULL; + gchar *error_state = nullptr; if (!dbus_g_proxy_call(proxy.gproxy(), debugd::kGetLog, &chromeos::Resetter(&error).lvalue(), G_TYPE_STRING, "i915_error_state", G_TYPE_INVALID, @@ -150,12 +150,12 @@ bool ChromeCollector::HandleCrash(const FilePath &file_path, FilePath dir; uid_t uid = atoi(uid_string.c_str()); pid_t pid = atoi(pid_string.c_str()); - if (!GetCreatedCrashDirectoryByEuid(uid, &dir, NULL)) { + if (!GetCreatedCrashDirectoryByEuid(uid, &dir, nullptr)) { LOG(ERROR) << "Can't create crash directory for uid " << uid; return false; } - std::string dump_basename = FormatDumpBasename(exe_name, time(NULL), pid); + std::string dump_basename = FormatDumpBasename(exe_name, time(nullptr), pid); FilePath meta_path = GetCrashPath(dir, dump_basename, "meta"); FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp"); FilePath log_path = GetCrashPath(dir, dump_basename, "log.tar.xz"); diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index fcb08450b..825ed08f2 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -88,8 +88,8 @@ CrashCollector::~CrashCollector() { void CrashCollector::Initialize( CrashCollector::CountCrashFunction count_crash_function, CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function) { - CHECK(count_crash_function != NULL); - CHECK(is_feedback_allowed_function != NULL); + CHECK(count_crash_function); + CHECK(is_feedback_allowed_function); count_crash_function_ = count_crash_function; is_feedback_allowed_function_ = is_feedback_allowed_function; @@ -156,7 +156,7 @@ const char *GetGErrorMessage(const GError *error) { } GHashTable *CrashCollector::GetActiveUserSessions() { - GHashTable *active_sessions = NULL; + GHashTable *active_sessions = nullptr; chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); if (!dbus.HasConnection()) { @@ -174,7 +174,7 @@ GHashTable *CrashCollector::GetActiveUserSessions() { } // Request all the active sessions. - GError *gerror = NULL; + GError *gerror = nullptr; if (!dbus_g_proxy_call(proxy.gproxy(), login_manager::kSessionManagerRetrieveActiveSessions, &gerror, G_TYPE_INVALID, @@ -243,10 +243,10 @@ bool CrashCollector::GetUserInfoFromName(const std::string &name, gid_t *gid) { char storage[256]; struct passwd passwd_storage; - struct passwd *passwd_result = NULL; + struct passwd *passwd_result = nullptr; if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage), - &passwd_result) != 0 || passwd_result == NULL) { + &passwd_result) != 0 || passwd_result == nullptr) { LOG(ERROR) << "Cannot find user named " << name; return false; } @@ -262,7 +262,7 @@ bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid, uid_t default_user_id; gid_t default_user_group; - if (out_of_capacity != NULL) *out_of_capacity = false; + if (out_of_capacity) *out_of_capacity = false; // For testing. if (!forced_crash_directory_.empty()) { @@ -308,7 +308,7 @@ bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid, } if (!CheckHasCapacity(*crash_directory)) { - if (out_of_capacity != NULL) *out_of_capacity = true; + if (out_of_capacity) *out_of_capacity = true; return false; } @@ -386,7 +386,7 @@ bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) { struct dirent* ent; bool full = false; std::set basenames; - while (readdir_r(dir, &ent_buf, &ent) == 0 && ent != NULL) { + while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) { if ((strcmp(ent->d_name, ".") == 0) || (strcmp(ent->d_name, "..") == 0)) continue; diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index c98d4a5ee..a1565e3bc 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -91,7 +91,7 @@ class CrashCollector { // Determines the crash directory for given euid, and creates the // directory if necessary with appropriate permissions. If - // |out_of_capacity| is not NULL, it is set to indicate if the call + // |out_of_capacity| is not nullptr, it is set to indicate if the call // failed due to not having capacity in the crash directory. Returns // true whether or not directory needed to be created, false on any // failure. If the crash directory is at capacity, returns false. diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index a483c847b..1da71d46a 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -172,7 +172,7 @@ static int HandleUserCrash(UserCollector *user_collector) { // Accumulate logs to help in diagnosing failures during user collection. chromeos::LogToString(true); // Handle the crash, get the name of the process from procfs. - bool handled = user_collector->HandleCrash(FLAGS_user, NULL); + bool handled = user_collector->HandleCrash(FLAGS_user, nullptr); chromeos::LogToString(false); if (!handled) return 1; diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index ab7e250fe..6ba529f2b 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -236,7 +236,8 @@ void KernelCollector::StripSensitiveData(std::string *kernel_dump) { } bool KernelCollector::Enable() { - if (arch_ == kArchUnknown || arch_ >= kArchCount || kPCRegex[arch_] == NULL) { + if (arch_ == kArchUnknown || arch_ >= kArchCount || + kPCRegex[arch_] == nullptr) { LOG(WARNING) << "KernelCollector does not understand this architecture"; return false; } @@ -519,12 +520,12 @@ bool KernelCollector::Collect() { if (!GetCreatedCrashDirectoryByEuid(kRootUid, &root_crash_directory, - NULL)) { + nullptr)) { return true; } std::string dump_basename = - FormatDumpBasename(kKernelExecName, time(NULL), kKernelPid); + FormatDumpBasename(kKernelExecName, time(nullptr), kKernelPid); FilePath kernel_crash_path = root_crash_directory.Append( StringPrintf("%s.kcrash", dump_basename.c_str())); diff --git a/crash_reporter/kernel_warning_collector.cc b/crash_reporter/kernel_warning_collector.cc index b4ff3ea3c..5dcd1f688 100644 --- a/crash_reporter/kernel_warning_collector.cc +++ b/crash_reporter/kernel_warning_collector.cc @@ -68,12 +68,13 @@ bool KernelWarningCollector::Collect() { } FilePath root_crash_directory; - if (!GetCreatedCrashDirectoryByEuid(kRootUid, &root_crash_directory, NULL)) { + if (!GetCreatedCrashDirectoryByEuid(kRootUid, &root_crash_directory, + nullptr)) { return true; } std::string dump_basename = - FormatDumpBasename(kExecName, time(NULL), kKernelPid); + FormatDumpBasename(kExecName, time(nullptr), kKernelPid); FilePath kernel_crash_path = root_crash_directory.Append( StringPrintf("%s.kcrash", dump_basename.c_str())); diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc index 282c6ae02..6cac8f8f4 100644 --- a/crash_reporter/list_proxies.cc +++ b/crash_reporter/list_proxies.cc @@ -109,9 +109,9 @@ class BrowserProxyResolvedSignalWatcher : public chromeos::dbus::SignalWatcher { void OnSignal(DBusMessage *message) override { // Get args - char *source_url = NULL; - char *proxy_list = NULL; - char *error = NULL; + char *source_url = nullptr; + char *proxy_list = nullptr; + char *error = nullptr; DBusError arg_error; dbus_error_init(&arg_error); if (!dbus_message_get_args(message, &arg_error, @@ -150,7 +150,7 @@ static gboolean HandleBrowserTimeout(void *data) { } static bool ShowBrowserProxies(std::string url, unsigned timeout) { - GMainLoop *main_loop = g_main_loop_new(NULL, false); + GMainLoop *main_loop = g_main_loop_new(nullptr, false); chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); if (!dbus.HasConnection()) { @@ -175,7 +175,7 @@ static bool ShowBrowserProxies(std::string url, unsigned timeout) { // Request the proxies for our URL. The answer is sent to us via a // proxy-resolved signal. - GError *gerror = NULL; + GError *gerror = nullptr; if (!dbus_g_proxy_call(browser_proxy.gproxy(), kLibCrosServiceResolveNetworkProxyMethodName, &gerror, diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc index 6847d2a11..e202fed88 100644 --- a/crash_reporter/udev_collector.cc +++ b/crash_reporter/udev_collector.cc @@ -57,13 +57,13 @@ bool UdevCollector::HandleCrash(const std::string &udev_event) { // Make sure the crash directory exists, or create it if it doesn't. FilePath crash_directory; - if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, NULL)) { + if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, nullptr)) { LOG(ERROR) << "Could not get crash directory."; return false; } // Create the destination path. std::string log_file_name = - FormatDumpBasename(basename, time(NULL), 0); + FormatDumpBasename(basename, time(nullptr), 0); FilePath crash_path = GetCrashPath(crash_directory, log_file_name, "log"); // Handle the crash. diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index a3c6f9c3c..9056a5c6d 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -164,7 +164,7 @@ bool UserCollector::GetIdFromStatus( return false; } const char *number = ids[kind].c_str(); - char *end_number = NULL; + char *end_number = nullptr; *id = strtol(number, &end_number, 10); if (*end_number != '\0') { return false; @@ -187,13 +187,13 @@ void UserCollector::EnqueueCollectionErrorLog(pid_t pid, const std::string &exec) { FilePath crash_path; LOG(INFO) << "Writing conversion problems as separate crash report."; - if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) { + if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, nullptr)) { LOG(ERROR) << "Could not even get log directory; out of space?"; return; } AddCrashMetaData("sig", kCollectionErrorSignature); AddCrashMetaData("error_type", GetErrorTypeSignature(error_type)); - std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); + std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid); std::string error_log = chromeos::GetLog(); FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog"); if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature, @@ -447,7 +447,7 @@ UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash( // been left around for diagnostics from a failed conversion attempt. // If we don't, existing files can cause forking to fail. base::DeleteFile(container_dir, true); - std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); + std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid); FilePath core_path = GetCrashPath(crash_path, dump_basename, "core"); FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp"); diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 5250c717b..778c06120 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -254,7 +254,7 @@ TEST_F(UserCollectorTest, HandleChromeCrashWithConsent) { TEST_F(UserCollectorTest, HandleSuppliedChromeCrashWithConsent) { s_metrics = true; - collector_.HandleCrash("0:2:chrome", NULL); + collector_.HandleCrash("0:2:chrome", nullptr); EXPECT_TRUE(FindLog( "Received crash notification for supplied_chrome[0] sig 2")); EXPECT_TRUE(FindLog(kChromeIgnoreMsg)); @@ -479,7 +479,7 @@ TEST_F(UserCollectorTest, ValidateProcFiles) { // maps file is empty FilePath maps_file = container_dir.Append("maps"); - ASSERT_EQ(0, base::WriteFile(maps_file, NULL, 0)); + ASSERT_EQ(0, base::WriteFile(maps_file, nullptr, 0)); ASSERT_TRUE(base::PathExists(maps_file)); EXPECT_FALSE(collector_.ValidateProcFiles(container_dir)); From f27140dd073af8f52d0992ca150c7a14159dcefb Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 17 Sep 2014 14:45:29 -0400 Subject: [PATCH 203/225] crash-reporter: log notification for chrome crashes The standard crash collector logs a warning message every time it receives a crash which makes reviewing system logs nice. The chrome collector does not do this which makes it a bit harder to track what the system is doing. Add such a message to the chrome collector too. BUG=chromium:415220 TEST=`cbuildbot amd64-generic-full` passes Change-Id: I2b4c9990d99b780ed06d72be40b6ce5b95c1ee51 Reviewed-on: https://chromium-review.googlesource.com/218593 Tested-by: Mike Frysinger Reviewed-by: Ben Chan Commit-Queue: Mike Frysinger --- crash_reporter/chrome_collector.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index b7fa7ca2e..71384078a 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -142,6 +142,9 @@ bool ChromeCollector::HandleCrash(const FilePath &file_path, if (!is_feedback_allowed_function_()) return true; + LOG(WARNING) << "Received crash notification for " << exe_name << "[" + << pid_string << "] user " << uid_string << " (called directly)"; + if (exe_name.find('/') != std::string::npos) { LOG(ERROR) << "exe_name contains illegal characters: " << exe_name; return false; From c76413993650f794d82ac41f18658de95eed5b3e Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Sun, 21 Sep 2014 18:53:40 -0700 Subject: [PATCH 204/225] crash-reporter: Clean up angle brackets in template types. C++11 no longer has the angle bracket pitfall in template types. BUG=None TEST=`FEATURES=test emerge-$BOARD crash-reporter` Change-Id: I25410c458f730d8e0a38237b3711439262a1b774 Reviewed-on: https://chromium-review.googlesource.com/219194 Reviewed-by: Mike Frysinger Commit-Queue: Ben Chan Tested-by: Ben Chan --- crash_reporter/udev_collector.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc index e202fed88..64ec3392c 100644 --- a/crash_reporter/udev_collector.cc +++ b/crash_reporter/udev_collector.cc @@ -33,9 +33,9 @@ bool UdevCollector::HandleCrash(const std::string &udev_event) { // Process the udev event string. // First get all the key-value pairs. - std::vector > udev_event_keyval; + std::vector> udev_event_keyval; base::SplitStringIntoKeyValuePairs(udev_event, '=', ':', &udev_event_keyval); - std::vector >::const_iterator iter; + std::vector>::const_iterator iter; std::map udev_event_map; for (iter = udev_event_keyval.begin(); iter != udev_event_keyval.end(); From 6f891c5b3fa232dee58339612184387f1eb583a5 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 24 Sep 2014 15:42:11 -0400 Subject: [PATCH 205/225] crash-reporter: fix pstore detection The current Enable logic looks for an existing crash rather than the crash dir itself, so when we boot up clean we get a warning that we do not support kernel crashes. BUG=chromium:417350 TEST=`FEATURES=test emerge-link crash-reporter` passes TEST=`cbuildbot amd64-generic-full` passes TEST=booted VM and checked syslogs Change-Id: I076a37889ea10ed30ae522eba007619b001ec787 Reviewed-on: https://chromium-review.googlesource.com/219751 Tested-by: Mike Frysinger Reviewed-by: Luigi Semenzato Commit-Queue: Mike Frysinger --- crash_reporter/kernel_collector.cc | 27 ++++++++++++++++++++++--- crash_reporter/kernel_collector.h | 2 ++ crash_reporter/kernel_collector_test.cc | 5 +++-- crash_reporter/kernel_collector_test.h | 18 +++++++++++++++++ 4 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 crash_reporter/kernel_collector_test.h diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 6ba529f2b..cd684ff49 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -5,6 +5,7 @@ #include "crash-reporter/kernel_collector.h" #include +#include #include #include @@ -17,6 +18,7 @@ using base::StringPrintf; namespace { const char kDefaultKernelStackSignature[] = "kernel-UnspecifiedStackSignature"; +const char kDumpParentPath[] = "/dev"; const char kDumpPath[] = "/dev/pstore"; const char kDumpFormat[] = "dmesg-ramoops-%zu"; const char kKernelExecName[] = "kernel"; @@ -235,6 +237,27 @@ void KernelCollector::StripSensitiveData(std::string *kernel_dump) { *kernel_dump = result.str(); } +bool KernelCollector::DumpDirMounted() { + struct stat st_parent; + if (stat(kDumpParentPath, &st_parent)) { + PLOG(WARNING) << "Could not stat " << kDumpParentPath; + return false; + } + + struct stat st_dump; + if (stat(kDumpPath, &st_dump)) { + PLOG(WARNING) << "Could not stat " << kDumpPath; + return false; + } + + if (st_parent.st_dev == st_dump.st_dev) { + LOG(WARNING) << "Dump dir " << kDumpPath << " not mounted"; + return false; + } + + return true; +} + bool KernelCollector::Enable() { if (arch_ == kArchUnknown || arch_ >= kArchCount || kPCRegex[arch_] == nullptr) { @@ -242,9 +265,7 @@ bool KernelCollector::Enable() { return false; } - FilePath ramoops_record; - GetRamoopsRecordPath(&ramoops_record, 0); - if (!base::PathExists(ramoops_record)) { + if (!DumpDirMounted()) { LOG(WARNING) << "Kernel does not support crash dumping"; return false; } diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h index 3899fb02c..c8aedfe70 100644 --- a/crash_reporter/kernel_collector.h +++ b/crash_reporter/kernel_collector.h @@ -62,6 +62,8 @@ class KernelCollector : public CrashCollector { FRIEND_TEST(KernelCollectorTest, StripSensitiveDataSample); FRIEND_TEST(KernelCollectorTest, CollectOK); + virtual bool DumpDirMounted(); + bool LoadPreservedDump(std::string *contents); void StripSensitiveData(std::string *kernel_dump); diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 654ed9de9..d920fc651 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "crash-reporter/kernel_collector.h" +#include "crash-reporter/kernel_collector_test.h" #include @@ -43,7 +43,7 @@ class KernelCollectorTest : public ::testing::Test { const FilePath &kcrash_file() const { return test_kcrash_; } const FilePath &test_crash_directory() const { return test_crash_directory_; } - KernelCollector collector_; + KernelCollectorMock collector_; private: void SetUp() override { @@ -99,6 +99,7 @@ TEST_F(KernelCollectorTest, EnableMissingKernel) { TEST_F(KernelCollectorTest, EnableOK) { WriteStringToFile(kcrash_file(), ""); + EXPECT_CALL(collector_, DumpDirMounted()).WillOnce(::testing::Return(true)); ASSERT_TRUE(collector_.Enable()); ASSERT_TRUE(collector_.is_enabled()); ASSERT_TRUE(FindLog("Enabling kernel crash handling")); diff --git a/crash_reporter/kernel_collector_test.h b/crash_reporter/kernel_collector_test.h new file mode 100644 index 000000000..32193246a --- /dev/null +++ b/crash_reporter/kernel_collector_test.h @@ -0,0 +1,18 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_ +#define CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_ + +#include "crash-reporter/kernel_collector.h" + +#include +#include + +class KernelCollectorMock : public KernelCollector { + public: + MOCK_METHOD0(DumpDirMounted, bool()); +}; + +#endif // CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_ From d6169a2a4f775da37ffa8b8ba4d767efd0377343 Mon Sep 17 00:00:00 2001 From: Steve Fung Date: Mon, 11 Aug 2014 15:52:23 -0700 Subject: [PATCH 206/225] crash-reporter: remove gflags dependency We are switching to using chromeos/flag_helper.h instead to standardize the code everywhere. BUG=chromium:402631 TEST=`FEATURES=test emerge-panther crash-reporter` TEST=`test_that -b panther e:logging_.*` Change-Id: I62b911a54f89d853235d0540460cbea119f66e9e Reviewed-on: https://chromium-review.googlesource.com/212140 Reviewed-by: Mike Frysinger Tested-by: Steve Fung Commit-Queue: Steve Fung --- crash_reporter/crash-reporter.gyp | 5 -- crash_reporter/crash_reporter.cc | 109 +++++++++++++++----------- crash_reporter/user_collector.cc | 28 +++---- crash_reporter/user_collector.h | 9 ++- crash_reporter/user_collector_test.cc | 5 +- 5 files changed, 89 insertions(+), 67 deletions(-) diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index 79faf97e5..974d8d018 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -29,11 +29,6 @@ '<@(exported_deps)', ], }, - 'link_settings': { - 'libraries': [ - '-lgflags', - ], - }, }, 'sources': [ 'chrome_collector.cc', diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index 1da71d46a..c5824aa84 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -8,14 +8,13 @@ #include #include -#include #include #include #include #include #include +#include #include -#include #include #include "crash-reporter/chrome_collector.h" @@ -25,22 +24,6 @@ #include "crash-reporter/unclean_shutdown_collector.h" #include "crash-reporter/user_collector.h" -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -DEFINE_bool(init, false, "Initialize crash logging"); -DEFINE_bool(clean_shutdown, false, "Signal clean shutdown"); -DEFINE_string(generate_kernel_signature, "", - "Generate signature from given kcrash file"); -DEFINE_bool(crash_test, false, "Crash test"); -DEFINE_string(user, "", "User crash info (pid:signal:exec_name)"); -DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); -DEFINE_string(udev, "", "Udev event description (type:device:subsystem)"); -DEFINE_bool(kernel_warning, false, "Report collected kernel warning"); -DEFINE_string(chrome, "", "Chrome crash dump file"); -DEFINE_string(pid, "", "PID of crashing process"); -DEFINE_string(uid, "", "UID of crashing process"); -DEFINE_string(exe, "", "Executable name of crashing process"); -#pragma GCC diagnostic error "-Wstrict-aliasing" - static const char kCrashCounterHistogram[] = "Logging.CrashCounter"; static const char kUserCrashSignal[] = "org.chromium.CrashReporter.UserCrash"; @@ -124,8 +107,10 @@ static void CountChromeCrash() { static int Initialize(KernelCollector *kernel_collector, UserCollector *user_collector, - UncleanShutdownCollector *unclean_shutdown_collector) { - CHECK(!FLAGS_clean_shutdown) << "Incompatible options"; + UncleanShutdownCollector *unclean_shutdown_collector, + const bool unclean_check, + const bool clean_shutdown) { + CHECK(!clean_shutdown) << "Incompatible options"; bool was_kernel_crash = false; bool was_unclean_shutdown = false; @@ -134,7 +119,7 @@ static int Initialize(KernelCollector *kernel_collector, was_kernel_crash = kernel_collector->Collect(); } - if (FLAGS_unclean_check) { + if (unclean_check) { was_unclean_shutdown = unclean_shutdown_collector->Collect(); } @@ -158,13 +143,14 @@ static int Initialize(KernelCollector *kernel_collector, return 0; } -static int HandleUserCrash(UserCollector *user_collector) { +static int HandleUserCrash(UserCollector *user_collector, + const std::string& user, const bool crash_test) { // Handle a specific user space crash. - CHECK(!FLAGS_user.empty()) << "--user= must be set"; + CHECK(!user.empty()) << "--user= must be set"; // Make it possible to test what happens when we crash while // handling a crash. - if (FLAGS_crash_test) { + if (crash_test) { *(volatile char *)0 = 0; return 0; } @@ -172,35 +158,40 @@ static int HandleUserCrash(UserCollector *user_collector) { // Accumulate logs to help in diagnosing failures during user collection. chromeos::LogToString(true); // Handle the crash, get the name of the process from procfs. - bool handled = user_collector->HandleCrash(FLAGS_user, nullptr); + bool handled = user_collector->HandleCrash(user, nullptr); chromeos::LogToString(false); if (!handled) return 1; return 0; } -static int HandleChromeCrash(ChromeCollector *chrome_collector) { - CHECK(!FLAGS_chrome.empty()) << "--chrome= must be set"; - CHECK(!FLAGS_pid.empty()) << "--pid= must be set"; - CHECK(!FLAGS_uid.empty()) << "--uid= must be set"; - CHECK(!FLAGS_exe.empty()) << "--exe= must be set"; +static int HandleChromeCrash(ChromeCollector *chrome_collector, + const std::string& chrome_dump_file, + const std::string& pid, + const std::string& uid, + const std::string& exe) { + CHECK(!chrome_dump_file.empty()) << "--chrome= must be set"; + CHECK(!pid.empty()) << "--pid= must be set"; + CHECK(!uid.empty()) << "--uid= must be set"; + CHECK(!exe.empty()) << "--exe= must be set"; chromeos::LogToString(true); - bool handled = chrome_collector->HandleCrash(FilePath(FLAGS_chrome), - FLAGS_pid, FLAGS_uid, FLAGS_exe); + bool handled = chrome_collector->HandleCrash(FilePath(chrome_dump_file), + pid, uid, exe); chromeos::LogToString(false); if (!handled) return 1; return 0; } -static int HandleUdevCrash(UdevCollector *udev_collector) { +static int HandleUdevCrash(UdevCollector *udev_collector, + const std::string& udev_event) { // Handle a crash indicated by a udev event. - CHECK(!FLAGS_udev.empty()) << "--udev= must be set"; + CHECK(!udev_event.empty()) << "--udev= must be set"; // Accumulate logs to help in diagnosing failures during user collection. chromeos::LogToString(true); - bool handled = udev_collector->HandleCrash(FLAGS_udev); + bool handled = udev_collector->HandleCrash(udev_event); chromeos::LogToString(false); if (!handled) return 1; @@ -219,10 +210,11 @@ static int HandleKernelWarning(KernelWarningCollector } // Interactive/diagnostics mode for generating kernel crash signatures. -static int GenerateKernelSignature(KernelCollector *kernel_collector) { +static int GenerateKernelSignature(KernelCollector *kernel_collector, + const std::string& kernel_signature_file) { std::string kcrash_contents; std::string signature; - if (!base::ReadFileToString(FilePath(FLAGS_generate_kernel_signature), + if (!base::ReadFileToString(FilePath(kernel_signature_file), &kcrash_contents)) { fprintf(stderr, "Could not read file.\n"); return 1; @@ -258,11 +250,28 @@ static void OpenStandardFileDescriptors() { } int main(int argc, char *argv[]) { + DEFINE_bool(init, false, "Initialize crash logging"); + DEFINE_bool(clean_shutdown, false, "Signal clean shutdown"); + DEFINE_string(generate_kernel_signature, "", + "Generate signature from given kcrash file"); + DEFINE_bool(crash_test, false, "Crash test"); + DEFINE_string(user, "", "User crash info (pid:signal:exec_name)"); + DEFINE_bool(unclean_check, true, "Check for unclean shutdown"); + DEFINE_string(udev, "", "Udev event description (type:device:subsystem)"); + DEFINE_bool(kernel_warning, false, "Report collected kernel warning"); + DEFINE_string(chrome, "", "Chrome crash dump file"); + DEFINE_string(pid, "", "PID of crashing process"); + DEFINE_string(uid, "", "UID of crashing process"); + DEFINE_string(exe, "", "Executable name of crashing process"); + DEFINE_bool(core2md_failure, false, "Core2md failure test"); + DEFINE_bool(directory_failure, false, "Spool directory failure test"); + DEFINE_string(filter_in, "", + "Ignore all crashes but this for testing"); + OpenStandardFileDescriptors(); - google::ParseCommandLineFlags(&argc, &argv, true); FilePath my_path = base::MakeAbsoluteFilePath(FilePath(argv[0])); s_metrics_lib.Init(); - CommandLine::Init(argc, argv); + chromeos::FlagHelper::Init(argc, argv, "Chromium OS Crash Reporter"); chromeos::OpenLog(my_path.BaseName().value().c_str(), true); chromeos::InitLog(chromeos::kLogToSyslog); @@ -274,7 +283,10 @@ int main(int argc, char *argv[]) { user_collector.Initialize(CountUserCrash, my_path.value(), IsFeedbackAllowed, - true); // generate_diagnostics + true, // generate_diagnostics + FLAGS_core2md_failure, + FLAGS_directory_failure, + FLAGS_filter_in); UncleanShutdownCollector unclean_shutdown_collector; unclean_shutdown_collector.Initialize(CountUncleanShutdown, IsFeedbackAllowed); @@ -289,7 +301,9 @@ int main(int argc, char *argv[]) { if (FLAGS_init) { return Initialize(&kernel_collector, &user_collector, - &unclean_shutdown_collector); + &unclean_shutdown_collector, + FLAGS_unclean_check, + FLAGS_clean_shutdown); } if (FLAGS_clean_shutdown) { @@ -299,11 +313,12 @@ int main(int argc, char *argv[]) { } if (!FLAGS_generate_kernel_signature.empty()) { - return GenerateKernelSignature(&kernel_collector); + return GenerateKernelSignature(&kernel_collector, + FLAGS_generate_kernel_signature); } if (!FLAGS_udev.empty()) { - return HandleUdevCrash(&udev_collector); + return HandleUdevCrash(&udev_collector, FLAGS_udev); } if (FLAGS_kernel_warning) { @@ -311,8 +326,12 @@ int main(int argc, char *argv[]) { } if (!FLAGS_chrome.empty()) { - return HandleChromeCrash(&chrome_collector); + return HandleChromeCrash(&chrome_collector, + FLAGS_chrome, + FLAGS_pid, + FLAGS_uid, + FLAGS_exe); } - return HandleUserCrash(&user_collector); + return HandleUserCrash(&user_collector, FLAGS_user, FLAGS_crash_test); } diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 9056a5c6d..302b1308a 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -26,14 +26,6 @@ #include #include #include -#include - -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -DEFINE_bool(core2md_failure, false, "Core2md failure test"); -DEFINE_bool(directory_failure, false, "Spool directory failure test"); -DEFINE_string(filter_in, "", - "Ignore all crashes but this for testing"); -#pragma GCC diagnostic error "-Wstrict-aliasing" static const char kCollectionErrorSignature[] = "crash_reporter-user-collection"; @@ -69,12 +61,18 @@ void UserCollector::Initialize( UserCollector::CountCrashFunction count_crash_function, const std::string &our_path, UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function, - bool generate_diagnostics) { + bool generate_diagnostics, + bool core2md_failure, + bool directory_failure, + const std::string &filter_in) { CrashCollector::Initialize(count_crash_function, is_feedback_allowed_function); our_path_ = our_path; initialized_ = true; generate_diagnostics_ = generate_diagnostics; + core2md_failure_ = core2md_failure; + directory_failure_ = directory_failure; + filter_in_ = filter_in; } UserCollector::~UserCollector() { @@ -301,7 +299,7 @@ bool UserCollector::GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid, bool *out_of_capacity) { FilePath process_path = GetProcessPath(pid); std::string status; - if (FLAGS_directory_failure) { + if (directory_failure_) { LOG(ERROR) << "Purposefully failing to create spool directory"; return false; } @@ -368,7 +366,7 @@ bool UserCollector::RunCoreToMinidump(const FilePath &core_path, core2md.AddArg(core_path.value()); core2md.AddArg(procfs_directory.value()); - if (!FLAGS_core2md_failure) { + if (!core2md_failure_) { core2md.AddArg(minidump_path.value()); } else { // To test how core2md errors are propagaged, cause an error @@ -635,13 +633,13 @@ bool UserCollector::HandleCrash(const std::string &crash_attributes, // Allow us to test the crash reporting mechanism successfully even if // other parts of the system crash. - if (!FLAGS_filter_in.empty() && - (FLAGS_filter_in == "none" || - FLAGS_filter_in != exec)) { + if (!filter_in_.empty() && + (filter_in_ == "none" || + filter_in_ != exec)) { // We use a different format message to make it more obvious in tests // which crashes are test generated and which are real. LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while " - << "filter_in=" << FLAGS_filter_in << "."; + << "filter_in=" << filter_in_ << "."; return true; } diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h index fe2c00b74..aac94cb84 100644 --- a/crash_reporter/user_collector.h +++ b/crash_reporter/user_collector.h @@ -30,7 +30,10 @@ class UserCollector : public CrashCollector { void Initialize(CountCrashFunction count_crash, const std::string &our_path, IsFeedbackAllowedFunction is_metrics_allowed, - bool generate_diagnostics); + bool generate_diagnostics, + bool core2md_failure, + bool directory_failure, + const std::string &filter_in); ~UserCollector() override; @@ -176,6 +179,10 @@ class UserCollector : public CrashCollector { std::string our_path_; bool initialized_; + bool core2md_failure_; + bool directory_failure_; + std::string filter_in_; + static const char *kUserId; static const char *kGroupId; diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 778c06120..4350f1806 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -42,7 +42,10 @@ class UserCollectorTest : public ::testing::Test { collector_.Initialize(CountCrash, kFilePath, IsMetrics, - false); + false, + false, + false, + ""); base::DeleteFile(FilePath("test"), true); mkdir("test", 0777); collector_.set_core_pattern_file("test/core_pattern"); From 8e5340a1d8a552a30dbf31acc3e885ded48d42a9 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Mon, 7 Jul 2014 17:39:47 -0700 Subject: [PATCH 207/225] crash-reporter: accept kernel dmesg records with no ramoops header pstore compression has been added since kernel 3.12. In order to decompress dmesg correctly, ramoops driver has to strip the header before handing over the record to the pstore driver, so we don't need to do it in KernelCollector anymore. The corresponding kernel patch is at https://chromium-review.googlesource.com/#/c/211389 BUG=chromium:392248 TEST=platform_KernelErrorPaths passed on 3.14 kernel Change-Id: If1bec43e640e0978c7573cc90befc6d68072373c Signed-off-by: Ben Zhang Reviewed-on: https://chromium-review.googlesource.com/211460 Reviewed-by: Kees Cook --- crash_reporter/kernel_collector.cc | 10 +++++++--- crash_reporter/kernel_collector_test.cc | 14 +++----------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index cd684ff49..98b3e8f26 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -96,14 +96,18 @@ bool KernelCollector::ReadRecordToString(std::string *contents, return false; } + *record_found = true; if (record_re.FullMatch(record, &captured)) { // Found a match, append it to the content, and remove from pstore. contents->append(captured); - base::DeleteFile(ramoops_record, false); - *record_found = true; } else { - *record_found = false; + // pstore compression has been added since kernel 3.12. In order to + // decompress dmesg correctly, ramoops driver has to strip the header + // before handing over the record to the pstore driver, so we don't + // need to do it here anymore. + contents->append(record); } + base::DeleteFile(ramoops_record, false); return true; } diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index d920fc651..6284f49ef 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -78,10 +78,10 @@ TEST_F(KernelCollectorTest, LoadPreservedDump) { std::string dump; dump.clear(); - WriteStringToFile(kcrash_file(), "emptydata"); + WriteStringToFile(kcrash_file(), "CrashRecordWithoutRamoopsHeader"); ASSERT_TRUE(collector_.LoadParameters()); - ASSERT_FALSE(collector_.LoadPreservedDump(&dump)); - ASSERT_EQ("", dump); + ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); + ASSERT_EQ("CrashRecordWithoutRamoopsHeader", dump); WriteStringToFile(kcrash_file(), "====1.1\nsomething"); ASSERT_TRUE(collector_.LoadParameters()); @@ -229,14 +229,6 @@ TEST_F(KernelCollectorTest, CollectPreservedFileMissing) { ASSERT_EQ(0, s_crashes); } -TEST_F(KernelCollectorTest, CollectNoCrash) { - WriteStringToFile(kcrash_file(), ""); - ASSERT_FALSE(collector_.Collect()); - ASSERT_TRUE(FindLog("No valid records found")); - ASSERT_FALSE(FindLog("Stored kcrash to ")); - ASSERT_EQ(0, s_crashes); -} - TEST_F(KernelCollectorTest, CollectBadDirectory) { WriteStringToFile(kcrash_file(), "====1.1\nsomething"); ASSERT_TRUE(collector_.Collect()); From 554c898d6adb22b7f9e8ed77770fe4a2ee0a3afe Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 24 Nov 2014 16:15:37 -0800 Subject: [PATCH 208/225] crash-reporter: disable device coredumps in verified mode Device coredumps should only be available in developer mode. BUG=None TEST=link boot with devcoredump series Signed-off-by: Kees Cook Change-Id: Ia86c32310887199b268b1f71221c6eb8a02f7827 Reviewed-on: https://chromium-review.googlesource.com/231386 Reviewed-by: Mike Frysinger --- crash_reporter/init/crash-reporter.conf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crash_reporter/init/crash-reporter.conf b/crash_reporter/init/crash-reporter.conf index 3463c60c7..19f2cdbce 100644 --- a/crash_reporter/init/crash-reporter.conf +++ b/crash_reporter/init/crash-reporter.conf @@ -12,6 +12,12 @@ start on starting system-services pre-start script mkdir -p /var/spool + + # Only allow device coredumps on a "developer system". + if ! is_developer_end_user; then + # consumer end-user - disable device coredumps, if driver exists. + echo 1 > /sys/class/devcoredump/disabled || true + fi end script # crash_reporter uses argv[0] as part of the command line for From 859ee45cd2d9feb5e937c0c06f9e6c230979136e Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Wed, 10 Dec 2014 12:52:31 -0800 Subject: [PATCH 209/225] Update libchrome to r307740 and fix build errors Updated libchrome, libchrome_crypto, metrics, feedback to the latest revisions from Chrome (r307740). Fixed build breaks due to the changes in upstream code: - scope_ptr no longer needs explicit PassAs() calls. - scope_ptr no longer has implicit conversion to T*. Must use scope_ptr::get() instead. - base/file_util.h moved to base/files/file_util.h - ARRAYSIZE_UNSAFE() removed in favor of arraysize() - base::AppendToFile() and base::WriteFileDescriptor() now return bool instead of the number of bytes written. - dbus::Bus::AddFilterFunction() now returns void. - C++11 features are enabled in libchromeos, so all targets linking with it now have to support C++11 - OVERRIDE macro is removed in favor of native C++11 'override' keyword. BUG=chromium:416628, chromium:411508 TEST=Build the world on x86, x64, ARM. The following builders were tried: x86-generic-full amd64-generic-full arm-generic-full amd64-generic-asan daisy-full nyan-full pre-cq-group daisy-release-group sandybridge-release-group pineview-release-group CQ-DEPEND=CL:234450,CL:234980 Change-Id: I374bebe2211d533c4431c82efb8be1cdcb1f405d Reviewed-on: https://chromium-review.googlesource.com/234440 Reviewed-by: Bertrand Simonnet Tested-by: Alex Vakulenko Reviewed-by: Mike Frysinger Commit-Queue: Alex Vakulenko --- crash_reporter/crash_collector.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index 825ed08f2..e009ee4c2 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -104,7 +104,7 @@ int CrashCollector::WriteNewFile(const FilePath &filename, return -1; } - int rv = base::WriteFileDescriptor(fd, data, size); + int rv = base::WriteFileDescriptor(fd, data, size) ? size : -1; IGNORE_EINTR(close(fd)); return rv; } From 1c5533d4cd99f19ec8a37aaa841caeba76f7005c Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Tue, 20 Jan 2015 17:26:31 -0800 Subject: [PATCH 210/225] crash-reporter: add a sanity check for kernel dmesg records On some devices, after a cold boot, a junk pstore record /dev/pstore/dmesg-ramoops-0 is created which is just a chunk of uninitialized memory containing random bits, and it's not the result of a kernel crash. The sanity check scans for the dmesg log level pattern to avoid creating junk .kcrash files. BUG=chromium:443764 TEST=platform_KernelErrorPaths with 3.8 and 3.14 kernel; check no kcrash file is created for random binary ramoops dump on stumpy. Change-Id: I83041436cd8e5e0c7c0015c529f462032ce82f30 Signed-off-by: Ben Zhang Reviewed-on: https://chromium-review.googlesource.com/242147 Reviewed-by: Olof Johansson Reviewed-by: Grant Grundler Reviewed-by: Mike Frysinger --- crash_reporter/kernel_collector.cc | 21 ++++++++++++++++----- crash_reporter/kernel_collector_test.cc | 10 ++++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc index 98b3e8f26..d86efbdcc 100644 --- a/crash_reporter/kernel_collector.cc +++ b/crash_reporter/kernel_collector.cc @@ -89,6 +89,8 @@ bool KernelCollector::ReadRecordToString(std::string *contents, "====\\d+\\.\\d+\n(.*)", pcrecpp::RE_Options().set_multiline(true).set_dotall(true)); + pcrecpp::RE sanity_check_re("\n<\\d+>\\[\\s*(\\d+\\.\\d+)\\]"); + FilePath ramoops_record; GetRamoopsRecordPath(&ramoops_record, current_record); if (!base::ReadFileToString(ramoops_record, &record)) { @@ -96,18 +98,27 @@ bool KernelCollector::ReadRecordToString(std::string *contents, return false; } - *record_found = true; + *record_found = false; if (record_re.FullMatch(record, &captured)) { - // Found a match, append it to the content, and remove from pstore. + // Found a ramoops header, so strip the header and append the rest. contents->append(captured); - } else { + *record_found = true; + } else if (sanity_check_re.PartialMatch(record.substr(0, 1024))) { // pstore compression has been added since kernel 3.12. In order to // decompress dmesg correctly, ramoops driver has to strip the header // before handing over the record to the pstore driver, so we don't - // need to do it here anymore. + // need to do it here anymore. However, the sanity check is needed because + // sometimes a pstore record is just a chunk of uninitialized memory which + // is not the result of a kernel crash. See crbug.com/443764 contents->append(record); + *record_found = true; + } else { + LOG(WARNING) << "Found invalid record at " << ramoops_record.value(); } - base::DeleteFile(ramoops_record, false); + + // Remove the record from pstore after it's found. + if (*record_found) + base::DeleteFile(ramoops_record, false); return true; } diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 6284f49ef..08bb8b0ac 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -78,15 +78,21 @@ TEST_F(KernelCollectorTest, LoadPreservedDump) { std::string dump; dump.clear(); - WriteStringToFile(kcrash_file(), "CrashRecordWithoutRamoopsHeader"); + WriteStringToFile(kcrash_file(), + "CrashRecordWithoutRamoopsHeader\n<6>[ 0.078852]"); ASSERT_TRUE(collector_.LoadParameters()); ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); - ASSERT_EQ("CrashRecordWithoutRamoopsHeader", dump); + ASSERT_EQ("CrashRecordWithoutRamoopsHeader\n<6>[ 0.078852]", dump); WriteStringToFile(kcrash_file(), "====1.1\nsomething"); ASSERT_TRUE(collector_.LoadParameters()); ASSERT_TRUE(collector_.LoadPreservedDump(&dump)); ASSERT_EQ("something", dump); + + WriteStringToFile(kcrash_file(), "\x01\x02\xfe\xff random blob"); + ASSERT_TRUE(collector_.LoadParameters()); + ASSERT_FALSE(collector_.LoadPreservedDump(&dump)); + ASSERT_EQ("", dump); } TEST_F(KernelCollectorTest, EnableMissingKernel) { From 8f5546adf995682142a3782036434c464ebfc885 Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Wed, 21 Jan 2015 16:24:08 -0700 Subject: [PATCH 211/225] crash-reporter: Attach Chrome logs to crash reports. When Chrome crashes, gather the last 20 lines of each of the latest two log files in /var/log/chrome and /home/chronos/user/log. Attach them as gzipped metadata using a "chrome.txt" key name and rename the GPU error state key to "i915_error_state.log.xz". (Re-landing after privacy review; originally reviewed at https://chromium-review.googlesource.com/216427. Changes since previous iteration include attaching the Chrome logs and the GPU state separately and redirecting stderr to /dev/null when listing user Chrome log files to avoid log spam in the not-logged-in case.) BUG=chromium:405732 TEST=triggered crashes and verified that logs were included in crash reports: b7c4d7f2bee32e2f, 6292f090468fcf28 CQ-DEPEND=CL:242492 Change-Id: I1642a8971f1373726e5b0e3977dbfdbcc2aa6667 Reviewed-on: https://chromium-review.googlesource.com/242457 Commit-Queue: Dan Erat Trybot-Ready: Dan Erat Tested-by: Dan Erat Reviewed-by: Mike Frysinger --- crash_reporter/chrome_collector.cc | 150 +++++++++++++++--------- crash_reporter/chrome_collector.h | 8 ++ crash_reporter/crash_reporter_logs.conf | 4 + 3 files changed, 105 insertions(+), 57 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 71384078a..8dd56fb4f 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -20,16 +20,23 @@ #include #include -const char kDefaultMinidumpName[] = "upload_file_minidump"; -const char kTarPath[] = "/bin/tar"; -// From //net/crash/collector/collector.h -const int kDefaultMaxUploadBytes = 1024 * 1024; - using base::FilePath; using base::StringPrintf; namespace { +const char kDefaultMinidumpName[] = "upload_file_minidump"; + +// Path to the gzip binary. +const char kGzipPath[] = "/bin/gzip"; + +// Filenames for logs attached to crash reports. Also used as metadata keys. +const char kChromeLogFilename[] = "chrome.txt"; +const char kGpuStateFilename[] = "i915_error_state.log.xz"; + +// From //net/crash/collector/collector.h +const int kDefaultMaxUploadBytes = 1024 * 1024; + // Extract a string delimited by the given character, from the given offset // into a source string. Returns false if the string is zero-sized or no // delimiter was found. @@ -42,8 +49,25 @@ bool GetDelimitedString(const std::string &str, char ch, size_t offset, return true; } -bool GetDriErrorState(const chromeos::dbus::Proxy &proxy, - const FilePath &error_state_path) { +// Gets the GPU's error state from debugd and writes it to |error_state_path|. +// Returns true on success. +bool GetDriErrorState(const FilePath &error_state_path) { + chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); + if (!dbus.HasConnection()) { + LOG(ERROR) << "Error connecting to system D-Bus"; + return false; + } + + chromeos::dbus::Proxy proxy(dbus, + debugd::kDebugdServiceName, + debugd::kDebugdServicePath, + debugd::kDebugdInterface); + if (!proxy) { + LOG(ERROR) << "Error creating D-Bus proxy to interface " + << "'" << debugd::kDebugdServiceName << "'"; + return false; + } + chromeos::glib::ScopedError error; gchar *error_state = nullptr; if (!dbus_g_proxy_call(proxy.gproxy(), debugd::kGetLog, @@ -88,44 +112,19 @@ bool GetDriErrorState(const chromeos::dbus::Proxy &proxy, return true; } -bool GetAdditionalLogs(const FilePath &log_path) { - chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); - if (!dbus.HasConnection()) { - LOG(ERROR) << "Error connecting to system D-Bus"; - return false; +// Gzip-compresses |path|, removes the original file, and returns the path of +// the new file. On failure, the original file is left alone and an empty path +// is returned. +FilePath GzipFile(const FilePath& path) { + chromeos::ProcessImpl proc; + proc.AddArg(kGzipPath); + proc.AddArg(path.value()); + const int res = proc.Run(); + if (res != 0) { + LOG(ERROR) << "Failed to gzip " << path.value(); + return FilePath(); } - - chromeos::dbus::Proxy proxy(dbus, - debugd::kDebugdServiceName, - debugd::kDebugdServicePath, - debugd::kDebugdInterface); - if (!proxy) { - LOG(ERROR) << "Error creating D-Bus proxy to interface " - << "'" << debugd::kDebugdServiceName << "'"; - return false; - } - - FilePath error_state_path = - log_path.DirName().Append("i915_error_state.log.xz"); - if (!GetDriErrorState(proxy, error_state_path)) - return false; - - chromeos::ProcessImpl tar_process; - tar_process.AddArg(kTarPath); - tar_process.AddArg("cfJ"); - tar_process.AddArg(log_path.value()); - tar_process.AddStringOption("-C", log_path.DirName().value()); - tar_process.AddArg(error_state_path.BaseName().value()); - int res = tar_process.Run(); - - base::DeleteFile(error_state_path, false); - - if (res || !base::PathExists(log_path)) { - LOG(ERROR) << "Could not tar file " << log_path.value(); - return false; - } - - return true; + return path.AddExtension(".gz"); } } // namespace @@ -161,7 +160,6 @@ bool ChromeCollector::HandleCrash(const FilePath &file_path, std::string dump_basename = FormatDumpBasename(exe_name, time(nullptr), pid); FilePath meta_path = GetCrashPath(dir, dump_basename, "meta"); FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp"); - FilePath log_path = GetCrashPath(dir, dump_basename, "log.tar.xz"); std::string data; if (!base::ReadFileToString(file_path, &data)) { @@ -174,19 +172,31 @@ bool ChromeCollector::HandleCrash(const FilePath &file_path, return false; } - if (GetAdditionalLogs(log_path)) { - int64_t minidump_size = 0; - int64_t log_size = 0; - if (base::GetFileSize(minidump_path, &minidump_size) && - base::GetFileSize(log_path, &log_size) && - minidump_size > 0 && log_size > 0 && - minidump_size + log_size < kDefaultMaxUploadBytes) { - AddCrashMetaData("log", log_path.value()); - } else { - LOG(INFO) << "Skipping logs upload to prevent discarding minidump " - "because of report size limit < " << minidump_size + log_size; - base::DeleteFile(log_path, false); + + int64_t report_size = 0; + base::GetFileSize(minidump_path, &report_size); + + // Keyed by crash metadata key name. + const std::map additional_logs = + GetAdditionalLogs(dir, dump_basename, exe_name); + for (auto it : additional_logs) { + int64_t file_size = 0; + if (!base::GetFileSize(it.second, &file_size)) { + PLOG(WARNING) << "Unable to get size of " << it.second.value(); + continue; } + if (report_size + file_size > kDefaultMaxUploadBytes) { + LOG(INFO) << "Skipping upload of " << it.second.value() << "(" + << file_size << "B) because report size would exceed limit (" + << kDefaultMaxUploadBytes << "B)"; + continue; + } + VLOG(1) << "Adding metadata: " << it.first << " -> " << it.second.value(); + // Call AddCrashMetaUploadFile() rather than AddCrashMetaData() here. The + // former adds a prefix to the key name; without the prefix, only the key + // "logs" appears to be displayed on the crash server. + AddCrashMetaUploadFile(it.first, it.second.value()); + report_size += file_size; } // We're done. @@ -298,5 +308,31 @@ bool ChromeCollector::ParseCrashLog(const std::string &data, return at == data.size(); } +std::map ChromeCollector::GetAdditionalLogs( + const FilePath &dir, + const std::string &basename, + const std::string &exe_name) { + std::map logs; + + // Run the command specified by the config file to gather logs. + const FilePath chrome_log_path = + GetCrashPath(dir, basename, kChromeLogFilename); + if (GetLogContents(log_config_path_, exe_name, chrome_log_path)) { + const FilePath compressed_path = GzipFile(chrome_log_path); + if (!compressed_path.empty()) + logs[kChromeLogFilename] = compressed_path; + else + base::DeleteFile(chrome_log_path, false /* recursive */); + } + + // Now get the GPU state from debugd. + const FilePath dri_error_state_path = + GetCrashPath(dir, basename, kGpuStateFilename); + if (GetDriErrorState(dri_error_state_path)) + logs[kGpuStateFilename] = dri_error_state_path; + + return logs; +} + // static const char ChromeCollector::kSuccessMagic[] = "_sys_cr_finished"; diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h index d8602b93b..566b5a8a9 100644 --- a/crash_reporter/chrome_collector.h +++ b/crash_reporter/chrome_collector.h @@ -48,6 +48,14 @@ class ChromeCollector : public CrashCollector { const base::FilePath &minidump, const std::string &basename); + // Writes additional logs for |exe_name| to files based on |basename| within + // |dir|. Crash report metadata key names and the corresponding file paths are + // returned. + std::map GetAdditionalLogs( + const base::FilePath &dir, + const std::string &basename, + const std::string &exe_name); + FILE *output_file_ptr_; DISALLOW_COPY_AND_ASSIGN(ChromeCollector); diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index e4ad207dc..34407708d 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -20,6 +20,10 @@ update_engine:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/ # so it is handled in the same way as update_engine. cros_installer:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/update_engine/.) | tail -c 50000 +# Dump the last 20 lines of the last two files in Chrome's system and user log +# directories. +chrome:for f in $(ls -1rt /var/log/chrome/chrome_[0-9]* | tail -2) $(ls -1rt /home/chronos/u-*/log/chrome_[0-9]* 2>/dev/null | tail -2); do echo "===$f (tail)==="; tail -20 $f; echo EOF; done + # The following rule is used for generating additional diagnostics when # collection of user crashes fails. This output should not be too large # as it is stored in memory. The output format specified for 'ps' is the From 7e3b76d81fca476e08394eb40cc4b1ca8708475c Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Mon, 26 Jan 2015 18:03:30 -0700 Subject: [PATCH 212/225] crash-reporter: Add session_manager logs to Chrome crashes. Attach the last twenty session_manager messages from /var/log/messages to Chrome crash reports. BUG=chromium:233833 TEST=manual: triggered a crash and checked chrome.txt in the crash report (1f8c6ff259947e7a) Change-Id: I733b448ccc6e01111556a5a632e185cc58dfd917 Reviewed-on: https://chromium-review.googlesource.com/243378 Tested-by: Dan Erat Reviewed-by: Mike Frysinger Reviewed-by: Chris Masone Commit-Queue: Dan Erat --- crash_reporter/crash_reporter_logs.conf | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 34407708d..7f543684a 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -14,15 +14,17 @@ # occurred and we should avoid running anything that might cause # another crash. Similarly these command block the notification of # the crash to parent processes, so commands should execute quickly. -# +# Commands cannot contain colons. + update_engine:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/update_engine/.) | tail -c 50000 + # The cros_installer output is logged into the update engine log file, # so it is handled in the same way as update_engine. cros_installer:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/update_engine/.) | tail -c 50000 # Dump the last 20 lines of the last two files in Chrome's system and user log -# directories. -chrome:for f in $(ls -1rt /var/log/chrome/chrome_[0-9]* | tail -2) $(ls -1rt /home/chronos/u-*/log/chrome_[0-9]* 2>/dev/null | tail -2); do echo "===$f (tail)==="; tail -20 $f; echo EOF; done +# directories, along with the last 20 messages from the session manager. +chrome:for f in $(ls -1rt /var/log/chrome/chrome_[0-9]* | tail -2) $(ls -1rt /home/chronos/u-*/log/chrome_[0-9]* 2>/dev/null | tail -2); do echo "===$f (tail)==="; tail -20 $f; echo EOF; echo; done; echo "===session_manager (tail)==="; awk '$3 ~ "^session_manager\[" { print }' /var/log/messages | tail -20; echo EOF # The following rule is used for generating additional diagnostics when # collection of user crashes fails. This output should not be too large From d257ea1cbeb0d61fe73a2aef8f3f8feca5402a3c Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Wed, 28 Jan 2015 10:23:28 -0700 Subject: [PATCH 213/225] crash-reporter: Clean up testing code. Link all of the crash reporter's tests into a single crash_reporter_test executable instead of building separate binaries that lead to a bunch of duplication. BUG=none TEST=tests pass CQ-DEPEND=I7f6623dd0ae36395efac89bdcfae54926ea6c918 Change-Id: I18e2d929d6545e7d05e88232bdaec089ae620544 Reviewed-on: https://chromium-review.googlesource.com/243940 Reviewed-by: Dan Erat Commit-Queue: Dan Erat Trybot-Ready: Dan Erat Tested-by: Dan Erat --- crash_reporter/chrome_collector_test.cc | 31 +++++------- crash_reporter/crash-reporter.gyp | 47 ++----------------- crash_reporter/crash_collector_test.cc | 12 ++--- crash_reporter/kernel_collector_test.cc | 16 +++---- crash_reporter/testrunner.cc | 13 +++++ crash_reporter/udev_collector_test.cc | 7 +-- .../unclean_shutdown_collector_test.cc | 24 +++++----- crash_reporter/user_collector_test.cc | 32 ++++++------- 8 files changed, 69 insertions(+), 113 deletions(-) create mode 100644 crash_reporter/testrunner.cc diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc index 0f3b4fb78..d44c82c50 100644 --- a/crash_reporter/chrome_collector_test.cc +++ b/crash_reporter/chrome_collector_test.cc @@ -6,26 +6,25 @@ #include -#include - #include #include #include #include -#include #include using base::FilePath; -static const char kCrashFormatGood[] = "value1:10:abcdefghijvalue2:5:12345"; -static const char kCrashFormatEmbeddedNewline[] = - "value1:10:abcd\r\nghijvalue2:5:12\n34"; -static const char kCrashFormatBad1[] = "value1:10:abcdefghijvalue2:6=12345"; -static const char kCrashFormatBad2[] = "value1:10:abcdefghijvalue2:512345"; -static const char kCrashFormatBad3[] = "value1:10::abcdefghijvalue2:5=12345"; -static const char kCrashFormatBad4[] = "value1:10:abcdefghijvalue2:4=12345"; +namespace { -static const char kCrashFormatWithFile[] = +const char kCrashFormatGood[] = "value1:10:abcdefghijvalue2:5:12345"; +const char kCrashFormatEmbeddedNewline[] = + "value1:10:abcd\r\nghijvalue2:5:12\n34"; +const char kCrashFormatBad1[] = "value1:10:abcdefghijvalue2:6=12345"; +const char kCrashFormatBad2[] = "value1:10:abcdefghijvalue2:512345"; +const char kCrashFormatBad3[] = "value1:10::abcdefghijvalue2:5=12345"; +const char kCrashFormatBad4[] = "value1:10:abcdefghijvalue2:4=12345"; + +const char kCrashFormatWithFile[] = "value1:10:abcdefghijvalue2:5:12345" "some_file\"; filename=\"foo.txt\":15:12345\n789\n12345" "value3:2:ok"; @@ -33,12 +32,14 @@ static const char kCrashFormatWithFile[] = void CountCrash() { } -static bool s_allow_crash = false; +bool s_allow_crash = false; bool IsMetrics() { return s_allow_crash; } +} // namespace + class ChromeCollectorTest : public ::testing::Test { protected: void ExpectFileEquals(const char *golden, @@ -139,9 +140,3 @@ TEST_F(ChromeCollectorTest, HandleCrash) { } ExpectFileEquals(ChromeCollector::kSuccessMagic, log_file); } - -int main(int argc, char **argv) { - ::g_type_init(); - SetUpTests(&argc, argv, false); - return RUN_ALL_TESTS(); -} diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index 974d8d018..b80924b1b 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -95,58 +95,21 @@ ['USE_test == 1', { 'targets': [ { - 'target_name': 'chrome_collector_test', + 'target_name': 'crash_reporter_test', 'type': 'executable', 'includes': ['../common-mk/common_test.gypi'], 'dependencies': ['libcrash'], 'sources': [ 'chrome_collector_test.cc', - ] - }, - { - 'target_name': 'crash_collector_test', - 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], - 'dependencies': ['libcrash'], - 'sources': [ 'crash_collector_test.cc', - ] - }, - { - 'target_name': 'kernel_collector_test', - 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], - 'dependencies': ['libcrash'], - 'sources': [ + 'crash_collector_test.h', 'kernel_collector_test.cc', - ] - }, - { - 'target_name': 'udev_collector_test', - 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], - 'dependencies': ['libcrash'], - 'sources': [ + 'kernel_collector_test.h', + 'testrunner.cc', 'udev_collector_test.cc', - ] - }, - { - 'target_name': 'unclean_shutdown_collector_test', - 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], - 'dependencies': ['libcrash'], - 'sources': [ 'unclean_shutdown_collector_test.cc', - ] - }, - { - 'target_name': 'user_collector_test', - 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], - 'dependencies': ['libcrash'], - 'sources': [ 'user_collector_test.cc', - ] + ], }, ], }], diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 7e4c7c12d..1548d708a 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -6,14 +6,12 @@ #include -#include #include #include #include #include #include -#include #include #include "crash-reporter/crash_collector.h" @@ -23,6 +21,8 @@ using base::StringPrintf; using chromeos::FindLog; using ::testing::Return; +namespace { + void CountCrash() { ADD_FAILURE(); } @@ -32,6 +32,8 @@ bool IsMetrics() { return false; } +} // namespace + class CrashCollectorTest : public ::testing::Test { public: void SetUp() { @@ -343,9 +345,3 @@ TEST_F(CrashCollectorTest, GetLogContents) { EXPECT_TRUE(base::ReadFileToString(output_file, &contents)); EXPECT_EQ("hello world\n", contents); } - -int main(int argc, char **argv) { - ::g_type_init(); - SetUpTests(&argc, argv, false); - return RUN_ALL_TESTS(); -} diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index 08bb8b0ac..b067afd7b 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -11,17 +11,18 @@ #include #include #include -#include #include -static int s_crashes = 0; -static bool s_metrics = false; - using base::FilePath; using base::StringPrintf; using chromeos::FindLog; using chromeos::GetLog; +namespace { + +int s_crashes = 0; +bool s_metrics = false; + void CountCrash() { ++s_crashes; } @@ -30,6 +31,8 @@ bool IsMetrics() { return s_metrics; } +} // namespace + class KernelCollectorTest : public ::testing::Test { protected: void WriteStringToFile(const FilePath &file_path, @@ -666,8 +669,3 @@ TEST_F(KernelCollectorTest, ComputeKernelStackSignatureX86) { ComputeKernelStackSignatureCommon(); } - -int main(int argc, char **argv) { - SetUpTests(&argc, argv, false); - return RUN_ALL_TESTS(); -} diff --git a/crash_reporter/testrunner.cc b/crash_reporter/testrunner.cc new file mode 100644 index 000000000..24e2329c1 --- /dev/null +++ b/crash_reporter/testrunner.cc @@ -0,0 +1,13 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include + +int main(int argc, char** argv) { + ::g_type_init(); + SetUpTests(&argc, argv, true); + return RUN_ALL_TESTS(); +} diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc index 148cf880a..66daaf12f 100644 --- a/crash_reporter/udev_collector_test.cc +++ b/crash_reporter/udev_collector_test.cc @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include "crash-reporter/udev_collector.h" @@ -102,8 +102,3 @@ TEST_F(UdevCollectorTest, TestMatches) { // TODO(sque, crosbug.com/32238) - test wildcard cases, multiple identical udev // events. - -int main(int argc, char **argv) { - SetUpTests(&argc, argv, false); - return RUN_ALL_TESTS(); -} diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc index f23174c10..859b88144 100644 --- a/crash_reporter/unclean_shutdown_collector_test.cc +++ b/crash_reporter/unclean_shutdown_collector_test.cc @@ -9,19 +9,20 @@ #include #include #include -#include #include -static int s_crashes = 0; -static bool s_metrics = true; - -static const char kTestDirectory[] = "test"; -static const char kTestSuspended[] = "test/suspended"; -static const char kTestUnclean[] = "test/unclean"; - using base::FilePath; using ::chromeos::FindLog; +namespace { + +int s_crashes = 0; +bool s_metrics = true; + +const char kTestDirectory[] = "test"; +const char kTestSuspended[] = "test/suspended"; +const char kTestUnclean[] = "test/unclean"; + void CountCrash() { ++s_crashes; } @@ -30,6 +31,8 @@ bool IsMetrics() { return s_metrics; } +} // namespace + class UncleanShutdownCollectorTest : public ::testing::Test { void SetUp() { s_crashes = 0; @@ -120,8 +123,3 @@ TEST_F(UncleanShutdownCollectorTest, CantDisable) { ASSERT_FALSE(collector_.Disable()); rmdir(kTestUnclean); } - -int main(int argc, char **argv) { - SetUpTests(&argc, argv, false); - return RUN_ALL_TESTS(); -} diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 4350f1806..5a5bb0cd4 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -12,22 +12,23 @@ #include #include #include -#include #include -static int s_crashes = 0; -static bool s_metrics = false; - -static const char kFilePath[] = "/my/path"; - -// Keep in sync with UserCollector::ShouldDump. -static const char kChromeIgnoreMsg[] = - "ignoring call by kernel - chrome crash; " - "waiting for chrome to call us directly"; - using base::FilePath; using chromeos::FindLog; +namespace { + +int s_crashes = 0; +bool s_metrics = false; + +const char kFilePath[] = "/my/path"; + +// Keep in sync with UserCollector::ShouldDump. +const char kChromeIgnoreMsg[] = + "ignoring call by kernel - chrome crash; " + "waiting for chrome to call us directly"; + void CountCrash() { ++s_crashes; } @@ -36,6 +37,8 @@ bool IsMetrics() { return s_metrics; } +} // namespace + class UserCollectorTest : public ::testing::Test { void SetUp() { s_crashes = 0; @@ -305,7 +308,7 @@ TEST_F(UserCollectorTest, GetExecutableBaseNameFromPid) { pid_t my_pid = getpid(); EXPECT_TRUE(collector_.GetExecutableBaseNameFromPid(my_pid, &base_name)); EXPECT_FALSE(FindLog("Readlink failed")); - EXPECT_EQ("user_collector_test", base_name); + EXPECT_EQ("crash_reporter_test", base_name); } TEST_F(UserCollectorTest, GetFirstLineWithPrefix) { @@ -539,8 +542,3 @@ TEST_F(UserCollectorTest, ValidateCoreFile) { EXPECT_EQ(UserCollector::kErrorInvalidCoreFile, collector_.ValidateCoreFile(core_file)); } - -int main(int argc, char **argv) { - SetUpTests(&argc, argv, false); - return RUN_ALL_TESTS(); -} From 731da3379bd7ead7222b1a7add45e307bec7c865 Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Wed, 28 Jan 2015 09:48:10 -0700 Subject: [PATCH 214/225] crash-reporter: Use chromeos::KeyValueStore. Make the crash reporter use libchromeos's KeyValueStore class to read its log-collection config file and /etc/lsb-release instead of its own parsing code. Also update the log-collection config file to split long commands across multiple lines and to use '=' instead of ':' to separate executable names from commands. BUG=chromium:452520 TEST=updated tests; also triggered powerd and chrome crashes and checked that logs were attached Change-Id: I4e2447712869608f32a4ae38f5d5cb9c6046af14 Reviewed-on: https://chromium-review.googlesource.com/244121 Reviewed-by: Dan Erat Commit-Queue: Dan Erat Tested-by: Dan Erat --- crash_reporter/crash-reporter.gyp | 1 + crash_reporter/crash_collector.cc | 101 ++++++++------------- crash_reporter/crash_collector.h | 11 --- crash_reporter/crash_collector_test.cc | 62 ++----------- crash_reporter/crash_reporter_logs.conf | 94 +++++++++++++------ crash_reporter/crash_reporter_logs_test.cc | 28 ++++++ crash_reporter/udev_collector_test.cc | 6 +- 7 files changed, 141 insertions(+), 162 deletions(-) create mode 100644 crash_reporter/crash_reporter_logs_test.cc diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index b80924b1b..b42657b6e 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -103,6 +103,7 @@ 'chrome_collector_test.cc', 'crash_collector_test.cc', 'crash_collector_test.h', + 'crash_reporter_logs_test.cc', 'kernel_collector_test.cc', 'kernel_collector_test.h', 'testrunner.cc', diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index e009ee4c2..a53bff9f4 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -30,19 +30,26 @@ #include #include #include +#include #include -static const char kCollectChromeFile[] = +namespace { + +const char kCollectChromeFile[] = "/mnt/stateful_partition/etc/collect_chrome_crashes"; -static const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress"; -static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf"; -static const char kDefaultUserName[] = "chronos"; -static const char kLeaveCoreFile[] = "/root/.leave_core"; -static const char kLsbRelease[] = "/etc/lsb-release"; -static const char kShellPath[] = "/bin/sh"; -static const char kSystemCrashPath[] = "/var/spool/crash"; -static const char kUploadVarPrefix[] = "upload_var_"; -static const char kUploadFilePrefix[] = "upload_file_"; +const char kCrashTestInProgressPath[] = "/tmp/crash-test-in-progress"; +const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf"; +const char kDefaultUserName[] = "chronos"; +const char kLeaveCoreFile[] = "/root/.leave_core"; +const char kLsbRelease[] = "/etc/lsb-release"; +const char kShellPath[] = "/bin/sh"; +const char kSystemCrashPath[] = "/var/spool/crash"; +const char kUploadVarPrefix[] = "upload_var_"; +const char kUploadFilePrefix[] = "upload_file_"; + +// Key of the lsb-release entry containing the OS version. +const char kLsbVersionKey[] = "CHROMEOS_RELEASE_VERSION"; + // Normally this path is not used. Unfortunately, there are a few edge cases // where we need this. Any process that runs as kDefaultUserName that crashes // is consider a "user crash". That includes the initial Chrome browser that @@ -53,16 +60,18 @@ static const char kUploadFilePrefix[] = "upload_file_"; // This also comes up when running autotests. The GUI is sitting at the login // screen while tests are sshing in, changing users, and triggering crashes as // the user (purposefully). -static const char kFallbackUserCrashPath[] = "/home/chronos/crash"; +const char kFallbackUserCrashPath[] = "/home/chronos/crash"; // Directory mode of the user crash spool directory. -static const mode_t kUserCrashPathMode = 0755; +const mode_t kUserCrashPathMode = 0755; // Directory mode of the system crash spool directory. -static const mode_t kSystemCrashPathMode = 01755; +const mode_t kSystemCrashPathMode = 01755; -static const uid_t kRootOwner = 0; -static const uid_t kRootGroup = 0; +const uid_t kRootOwner = 0; +const uid_t kRootGroup = 0; + +} // namespace // Maximum crash reports per crash spool directory. Note that this is // a separate maximum from the maximum rate at which we upload these @@ -416,65 +425,28 @@ bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) { return !full; } -bool CrashCollector::IsCommentLine(const std::string &line) { - size_t found = line.find_first_not_of(" "); - return found != std::string::npos && line[found] == '#'; -} - -bool CrashCollector::ReadKeyValueFile( - const FilePath &path, - const char separator, - std::map *dictionary) { - std::string contents; - if (!base::ReadFileToString(path, &contents)) { - return false; - } - typedef std::vector StringVector; - StringVector lines; - base::SplitString(contents, '\n', &lines); - bool any_errors = false; - for (StringVector::iterator line = lines.begin(); line != lines.end(); - ++line) { - // Allow empty strings. - if (line->empty()) - continue; - // Allow comment lines. - if (IsCommentLine(*line)) - continue; - StringVector sides; - base::SplitString(*line, separator, &sides); - if (sides.size() != 2) { - any_errors = true; - continue; - } - dictionary->insert(std::pair(sides[0], sides[1])); - } - return !any_errors; -} - bool CrashCollector::GetLogContents(const FilePath &config_path, const std::string &exec_name, const FilePath &output_file) { - std::map log_commands; - if (!ReadKeyValueFile(config_path, ':', &log_commands)) { + chromeos::KeyValueStore store; + if (!store.Load(config_path)) { LOG(INFO) << "Unable to read log configuration file " << config_path.value(); return false; } - if (log_commands.find(exec_name) == log_commands.end()) + std::string command; + if (!store.GetString(exec_name, &command)) return false; chromeos::ProcessImpl diag_process; diag_process.AddArg(kShellPath); - std::string shell_command = log_commands[exec_name]; - diag_process.AddStringOption("-c", shell_command); + diag_process.AddStringOption("-c", command); diag_process.RedirectOutput(output_file.value()); - int result = diag_process.Run(); + const int result = diag_process.Run(); if (result != 0) { - LOG(INFO) << "Running shell command " << shell_command << "failed with: " - << result; + LOG(INFO) << "Log command \"" << command << "\" exited with " << result; return false; } return true; @@ -500,15 +472,16 @@ void CrashCollector::AddCrashMetaUploadData(const std::string &key, void CrashCollector::WriteCrashMetaData(const FilePath &meta_path, const std::string &exec_name, const std::string &payload_path) { - std::map contents; - if (!ReadKeyValueFile(FilePath(lsb_release_), '=', &contents)) { + chromeos::KeyValueStore store; + if (!store.Load(FilePath(lsb_release_))) { LOG(ERROR) << "Problem parsing " << lsb_release_; // Even though there was some failure, take as much as we could read. } + std::string version("unknown"); - std::map::iterator i; - if ((i = contents.find("CHROMEOS_RELEASE_VERSION")) != contents.end()) { - version = i->second; + if (!store.GetString(kLsbVersionKey, &version)) { + LOG(ERROR) << "Unable to read " << kLsbVersionKey << " from " + << lsb_release_; } int64_t payload_size = -1; base::GetFileSize(FilePath(payload_path), &payload_size); diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index a1565e3bc..68806d9ed 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -43,10 +43,8 @@ class CrashCollector { FRIEND_TEST(CrashCollectorTest, ForkExecAndPipe); FRIEND_TEST(CrashCollectorTest, FormatDumpBasename); FRIEND_TEST(CrashCollectorTest, Initialize); - FRIEND_TEST(CrashCollectorTest, IsCommentLine); FRIEND_TEST(CrashCollectorTest, IsUserSpecificDirectoryEnabled); FRIEND_TEST(CrashCollectorTest, MetaData); - FRIEND_TEST(CrashCollectorTest, ReadKeyValueFile); FRIEND_TEST(CrashCollectorTest, Sanitize); FRIEND_TEST(CrashCollectorTest, WriteNewFile); FRIEND_TEST(ForkExecAndPipeTest, Basic); @@ -120,15 +118,6 @@ class CrashCollector { // crash. bool CheckHasCapacity(const base::FilePath &crash_directory); - // Checks if the line starts with '#' after optional whitespace. - static bool IsCommentLine(const std::string &line); - - // Read the given file of form [\n...] and return - // a map of its contents. - bool ReadKeyValueFile(const base::FilePath &file, - char separator, - std::map *dictionary); - // Write a log applicable to |exec_name| to |output_file| based on the // log configuration file at |config_path|. bool GetLogContents(const base::FilePath &config_path, diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 1548d708a..0ca379262 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -220,59 +220,6 @@ TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) { EXPECT_FALSE(CheckHasCapacity()); } -TEST_F(CrashCollectorTest, IsCommentLine) { - EXPECT_FALSE(CrashCollector::IsCommentLine("")); - EXPECT_TRUE(CrashCollector::IsCommentLine("#")); - EXPECT_TRUE(CrashCollector::IsCommentLine("#real comment")); - EXPECT_TRUE(CrashCollector::IsCommentLine(" # real comment")); - EXPECT_FALSE(CrashCollector::IsCommentLine("not comment")); - EXPECT_FALSE(CrashCollector::IsCommentLine(" not comment")); -} - -TEST_F(CrashCollectorTest, ReadKeyValueFile) { - const char *contents = ("a=b\n" - "\n" - " c=d \n"); - FilePath path(test_dir_.Append("keyval")); - std::map dictionary; - std::map::iterator i; - - base::WriteFile(path, contents, strlen(contents)); - - EXPECT_TRUE(collector_.ReadKeyValueFile(path, '=', &dictionary)); - i = dictionary.find("a"); - EXPECT_TRUE(i != dictionary.end() && i->second == "b"); - i = dictionary.find("c"); - EXPECT_TRUE(i != dictionary.end() && i->second == "d"); - - dictionary.clear(); - - contents = ("a=b c d\n" - "e\n" - " f g = h\n" - "i=j\n" - "=k\n" - "#comment=0\n" - "l=\n"); - base::WriteFile(path, contents, strlen(contents)); - - EXPECT_FALSE(collector_.ReadKeyValueFile(path, '=', &dictionary)); - EXPECT_EQ(5, dictionary.size()); - - i = dictionary.find("a"); - EXPECT_TRUE(i != dictionary.end() && i->second == "b c d"); - i = dictionary.find("e"); - EXPECT_TRUE(i == dictionary.end()); - i = dictionary.find("f g"); - EXPECT_TRUE(i != dictionary.end() && i->second == "h"); - i = dictionary.find("i"); - EXPECT_TRUE(i != dictionary.end() && i->second == "j"); - i = dictionary.find(""); - EXPECT_TRUE(i != dictionary.end() && i->second == "k"); - i = dictionary.find("l"); - EXPECT_TRUE(i != dictionary.end() && i->second == ""); -} - TEST_F(CrashCollectorTest, MetaData) { const char kMetaFileBasename[] = "generated.meta"; FilePath meta_file = test_dir_.Append(kMetaFileBasename); @@ -280,7 +227,10 @@ TEST_F(CrashCollectorTest, MetaData) { FilePath payload_file = test_dir_.Append("payload-file"); std::string contents; collector_.lsb_release_ = lsb_release.value(); - const char kLsbContents[] = "CHROMEOS_RELEASE_VERSION=version\n"; + const char kLsbContents[] = + "CHROMEOS_RELEASE_BOARD=lumpy\n" + "CHROMEOS_RELEASE_VERSION=6727.0.2015_01_26_0853\n" + "CHROMEOS_RELEASE_NAME=Chromium OS\n"; ASSERT_TRUE(base::WriteFile(lsb_release, kLsbContents, strlen(kLsbContents))); const char kPayload[] = "foo"; ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload))); @@ -290,7 +240,7 @@ TEST_F(CrashCollectorTest, MetaData) { const char kExpectedMeta[] = "foo=bar\n" "exec_name=kernel\n" - "ver=version\n" + "ver=6727.0.2015_01_26_0853\n" "payload=test/payload-file\n" "payload_size=3\n" "done=1\n"; @@ -328,7 +278,7 @@ TEST_F(CrashCollectorTest, GetLogContents) { FilePath config_file = test_dir_.Append("crash_config"); FilePath output_file = test_dir_.Append("crash_log"); const char kConfigContents[] = - "foobar:echo hello there | sed -e \"s/there/world/\""; + "foobar=echo hello there | \\\n sed -e \"s/there/world/\""; ASSERT_TRUE( base::WriteFile(config_file, kConfigContents, strlen(kConfigContents))); base::DeleteFile(FilePath(output_file), false); diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 7f543684a..1a69032fb 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -2,64 +2,102 @@ # Use of this source code is governed by a BSD-style license that can # be found in the LICENSE file. -# This file has the format: -# :\n +# This file is parsed by chromeos::KeyValueStore. It has the format: # -# Where when any executable with the basename crashes, the -# given is executed and its standard output and -# standard error is sent along with the crash report. +# =\n # -# Use caution in modifying this file. Only run common unix commands -# here as these commands will be run when a crash has recently -# occurred and we should avoid running anything that might cause -# another crash. Similarly these command block the notification of -# the crash to parent processes, so commands should execute quickly. -# Commands cannot contain colons. +# Commands may be split across multiple lines using trailing backslashes. +# +# When an executable named crashes, the corresponding command is +# executed and its standard output and standard error are attached to the crash +# report. +# +# Use caution in modifying this file. Only run common Unix commands here, as +# these commands will be run when a crash has recently occurred and we should +# avoid running anything that might cause another crash. Similarly, these +# commands block notification of the crash to parent processes, so commands +# should execute quickly. -update_engine:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/update_engine/.) | tail -c 50000 +update_engine=cat $(ls -1tr /var/log/update_engine | tail -5 | \ + sed s.^./var/log/update_engine/.) | tail -c 50000 # The cros_installer output is logged into the update engine log file, # so it is handled in the same way as update_engine. -cros_installer:cat $(ls -1tr /var/log/update_engine | tail -5 | sed s.^./var/log/update_engine/.) | tail -c 50000 +cros_installer=cat $(ls -1tr /var/log/update_engine | tail -5 | \ + sed s.^./var/log/update_engine/.) | tail -c 50000 # Dump the last 20 lines of the last two files in Chrome's system and user log # directories, along with the last 20 messages from the session manager. -chrome:for f in $(ls -1rt /var/log/chrome/chrome_[0-9]* | tail -2) $(ls -1rt /home/chronos/u-*/log/chrome_[0-9]* 2>/dev/null | tail -2); do echo "===$f (tail)==="; tail -20 $f; echo EOF; echo; done; echo "===session_manager (tail)==="; awk '$3 ~ "^session_manager\[" { print }' /var/log/messages | tail -20; echo EOF +chrome=\ + for f in $(ls -1rt /var/log/chrome/chrome_[0-9]* | tail -2) \ + $(ls -1rt /home/chronos/u-*/log/chrome_[0-9]* 2>/dev/null | tail -2); do \ + echo "===$f (tail)==="; \ + tail -20 $f; \ + echo EOF; \ + echo; \ + done; \ + echo "===session_manager (tail)==="; \ + awk '$3 ~ "^session_manager\[" { print }' /var/log/messages | tail -20; \ + echo EOF # The following rule is used for generating additional diagnostics when # collection of user crashes fails. This output should not be too large # as it is stored in memory. The output format specified for 'ps' is the # same as with the "u" ("user-oriented") option, except it doesn't show # the commands' arguments (i.e. "comm" instead of "command"). -crash_reporter-user-collection:echo "===ps output==="; ps axw -o user,pid,%cpu,%mem,vsz,rss,tname,stat,start_time,bsdtime,comm | tail -c 25000; echo "===meminfo==="; cat /proc/meminfo +crash_reporter-user-collection=\ + echo "===ps output==="; \ + ps axw -o user,pid,%cpu,%mem,vsz,rss,tname,stat,start_time,bsdtime,comm | \ + tail -c 25000; \ + echo "===meminfo==="; \ + cat /proc/meminfo # This rule is similar to the crash_reporter-user-collection rule, except it is # run for kernel errors reported through udev events. -crash_reporter-udev-collection-change-card0-drm:for dri in /sys/kernel/debug/dri/*; do echo "===$dri/i915_error_state==="; cat $dri/i915_error_state; done +crash_reporter-udev-collection-change-card0-drm=\ + for dri in /sys/kernel/debug/dri/*; do \ + echo "===$dri/i915_error_state==="; \ + cat $dri/i915_error_state; \ + done # When trackpad driver cyapa detects some abnormal behavior, we collect # additional logs from kernel messages. -crash_reporter-udev-collection-change--i2c-cyapa:/usr/sbin/kernel_log_collector.sh cyapa 30 -# When trackpad/touchscreen driver atmel_mxt_ts detects some abnormal behavior, we collect -# additional logs from kernel messages. -crash_reporter-udev-collection-change--i2c-atmel_mxt_ts:/usr/sbin/kernel_log_collector.sh atmel 30 -# When touch device noise are detected, we collect relevant logs. (crosbug.com/p/16788) -crash_reporter-udev-collection---TouchNoise:cat /var/log/touch_noise.log +crash_reporter-udev-collection-change--i2c-cyapa=\ + /usr/sbin/kernel_log_collector.sh cyapa 30 +# When trackpad/touchscreen driver atmel_mxt_ts detects some abnormal behavior, +# we collect additional logs from kernel messages. +crash_reporter-udev-collection-change--i2c-atmel_mxt_ts=\ + /usr/sbin/kernel_log_collector.sh atmel 30 +# When touch device noise are detected, we collect relevant logs. +# (crosbug.com/p/16788) +crash_reporter-udev-collection---TouchNoise=cat /var/log/touch_noise.log # Periodically collect touch event log for debugging (crosbug.com/p/17244) -crash_reporter-udev-collection---TouchEvent:cat /var/log/touch_event.log +crash_reporter-udev-collection---TouchEvent=cat /var/log/touch_event.log # Dump the last 50 lines of the last two powerd log files -- if the job has # already restarted, we want to see the end of the previous instance's logs. -powerd:for f in $(ls -1tr /var/log/power_manager/powerd.[0-9]* | tail -2); do echo "===$(basename $f) (tail)==="; tail -50 $f; echo EOF; done +powerd=\ + for f in $(ls -1tr /var/log/power_manager/powerd.[0-9]* | tail -2); do \ + echo "===$(basename $f) (tail)==="; \ + tail -50 $f; \ + echo EOF; \ + done # If power_supply_info aborts (due to e.g. a bad battery), its failure message # could end up in various places depending on which process was running it. # Attach the end of powerd's log since it might've also logged the underlying # problem. -power_supply_info:echo "===powerd.LATEST (tail)==="; tail -50 /var/log/power_manager/powerd.LATEST; echo EOF +power_supply_info=\ + echo "===powerd.LATEST (tail)==="; \ + tail -50 /var/log/power_manager/powerd.LATEST; \ + echo EOF # powerd_setuid_helper gets run by powerd, so its stdout/stderr will be mixed in # with powerd's stdout/stderr. -powerd_setuid_helper:echo "===powerd.OUT (tail)==="; tail -50 /var/log/powerd.out; echo EOF +powerd_setuid_helper=\ + echo "===powerd.OUT (tail)==="; \ + tail -50 /var/log/powerd.out; \ + echo EOF # The following rules are only for testing purposes. -crash_log_test:echo hello world -crash_log_recursion_test:sleep 1 && /usr/local/autotest/tests/crash_log_recursion_test +crash_log_test=echo hello world +crash_log_recursion_test=sleep 1 && \ + /usr/local/autotest/tests/crash_log_recursion_test diff --git a/crash_reporter/crash_reporter_logs_test.cc b/crash_reporter/crash_reporter_logs_test.cc new file mode 100644 index 000000000..987947093 --- /dev/null +++ b/crash_reporter/crash_reporter_logs_test.cc @@ -0,0 +1,28 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include +#include +#include + +namespace { + +// Name of the checked-in configuration file containing log-collection commands. +const char kConfigFile[] = "crash_reporter_logs.conf"; + +// Executable name for Chrome. kConfigFile is expected to contain this entry. +const char kChromeExecName[] = "chrome"; + +} // namespace + +// Tests that the config file is parsable and that Chrome is listed. +TEST(CrashReporterLogsTest, ReadConfig) { + chromeos::KeyValueStore store; + ASSERT_TRUE(store.Load(base::FilePath(kConfigFile))); + std::string command; + EXPECT_TRUE(store.GetString(kChromeExecName, &command)); + EXPECT_FALSE(command.empty()); +} diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc index 66daaf12f..f41b06f22 100644 --- a/crash_reporter/udev_collector_test.cc +++ b/crash_reporter/udev_collector_test.cc @@ -19,9 +19,9 @@ const char kLogConfigFileName[] = "log_config_file"; // A bunch of random rules to put into the dummy log config file. const char kLogConfigFileContents[] = - "crash_reporter-udev-collection-change-card0-drm:echo change card0 drm\n" - "crash_reporter-udev-collection-add-state0-cpu:echo change state0 cpu\n" - "cros_installer:echo not for udev"; + "crash_reporter-udev-collection-change-card0-drm=echo change card0 drm\n" + "crash_reporter-udev-collection-add-state0-cpu=echo change state0 cpu\n" + "cros_installer=echo not for udev"; void CountCrash() {} From efa91c05b1ab47ecf32ee2f463e7e8817bd9df2d Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Wed, 28 Jan 2015 21:34:29 -0700 Subject: [PATCH 215/225] crash: Shorten crash-reporter project alias. Add a .project_alias file so that crash reporter changes can/must be prefixed with "crash: " rather than "crash-reporter: ". BUG=none TEST=uploaded this change Change-Id: Iea849873cb061650909eecdc911d8805f17bc4c5 Reviewed-on: https://chromium-review.googlesource.com/244240 Reviewed-by: Ben Chan Reviewed-by: Dan Erat Commit-Queue: Dan Erat Tested-by: Dan Erat --- crash_reporter/.project_alias | 1 + 1 file changed, 1 insertion(+) create mode 100644 crash_reporter/.project_alias diff --git a/crash_reporter/.project_alias b/crash_reporter/.project_alias new file mode 100644 index 000000000..0bc3798b9 --- /dev/null +++ b/crash_reporter/.project_alias @@ -0,0 +1 @@ +crash From 6e13952269ad729594f2670b889bd806e9b0f3b3 Mon Sep 17 00:00:00 2001 From: Steve Fung Date: Thu, 5 Feb 2015 14:54:16 -0800 Subject: [PATCH 216/225] crash: Remove glib from crash_reporter As part of the minimization effort, refactor crash_reporter code to not depend directly on glib. BUG=brillo:87, brillo:88, chromium:435314 TEST=`FEATURES=test emerge-panther libchromeos debugd crash-reporter` TEST=Enabled crash reports; Browsed to chrome://crash; crash files \ generated; `FORCE_OFFICIAL=1 SECONDS_SEND_SPREAD=1 crash_sender` \ /var/log/messages shows crash id, report shows all expected files TEST=`cbuildbot --remote -p chromiumos/platform2 amd64-generic-full` CQ-DEPEND=I00331e0bf29195b41cd84d4495ab47738a5a41de CQ-DEPEND=I9df752d8995773adb56fab34dd97626f3ddf1765 Change-Id: I48b366198a7f89ca55259603cf8470e4d59321bf Reviewed-on: https://chromium-review.googlesource.com/246441 Reviewed-by: Dan Erat Tested-by: Steve Fung Commit-Queue: Steve Fung --- crash_reporter/chrome_collector.cc | 88 +++++++++---------- crash_reporter/chrome_collector.h | 8 ++ crash_reporter/chrome_collector_test.cc | 10 ++- crash_reporter/crash-reporter.gyp | 22 +++++ crash_reporter/crash_collector.cc | 85 +++++++----------- crash_reporter/crash_collector.h | 17 +++- crash_reporter/crash_collector_test.cc | 24 ++--- crash_reporter/crash_collector_test.h | 7 +- crash_reporter/crash_reporter.cc | 3 - crash_reporter/kernel_collector_test.cc | 3 + crash_reporter/kernel_collector_test.h | 1 + crash_reporter/testrunner.cc | 2 - crash_reporter/udev_collector_test.cc | 10 ++- .../unclean_shutdown_collector_test.cc | 12 ++- crash_reporter/user_collector_test.cc | 11 ++- 15 files changed, 180 insertions(+), 123 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index 8dd56fb4f..e32ef5670 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -4,10 +4,10 @@ #include "crash-reporter/chrome_collector.h" -#include #include #include +#include #include #include @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -51,39 +52,20 @@ bool GetDelimitedString(const std::string &str, char ch, size_t offset, // Gets the GPU's error state from debugd and writes it to |error_state_path|. // Returns true on success. -bool GetDriErrorState(const FilePath &error_state_path) { - chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); - if (!dbus.HasConnection()) { - LOG(ERROR) << "Error connecting to system D-Bus"; +bool GetDriErrorState(const FilePath &error_state_path, + org::chromium::debugdProxy *proxy) { + chromeos::ErrorPtr error; + std::string error_state_str; + + proxy->GetLog("i915_error_state", &error_state_str, &error); + + if (error) { + LOG(ERROR) << "Error calling D-Bus proxy call to interface " + << "'" << proxy->GetObjectPath().value() << "':" + << error->GetMessage(); return false; } - chromeos::dbus::Proxy proxy(dbus, - debugd::kDebugdServiceName, - debugd::kDebugdServicePath, - debugd::kDebugdInterface); - if (!proxy) { - LOG(ERROR) << "Error creating D-Bus proxy to interface " - << "'" << debugd::kDebugdServiceName << "'"; - return false; - } - - chromeos::glib::ScopedError error; - gchar *error_state = nullptr; - if (!dbus_g_proxy_call(proxy.gproxy(), debugd::kGetLog, - &chromeos::Resetter(&error).lvalue(), - G_TYPE_STRING, "i915_error_state", G_TYPE_INVALID, - G_TYPE_STRING, &error_state, G_TYPE_INVALID)) { - LOG(ERROR) << "Error performing D-Bus proxy call " - << "'" << debugd::kGetLog << "'" - << ": " << (error ? error->message : ""); - g_free(error_state); - return false; - } - - std::string error_state_str(error_state); - g_free(error_state); - if (error_state_str == "") return false; @@ -94,17 +76,23 @@ bool GetDriErrorState(const FilePath &error_state_path) { return false; } - gsize len; - guchar *decoded_error_state = - g_base64_decode(error_state_str.c_str() + kBase64HeaderLength, &len); + std::string decoded_error_state; - int written = - base::WriteFile(error_state_path, - reinterpret_cast(decoded_error_state), len); - g_free(decoded_error_state); + if (!chromeos::data_encoding::Base64Decode( + error_state_str.c_str() + kBase64HeaderLength, + &decoded_error_state)) { + LOG(ERROR) << "Could not decode i915_error_state"; + return false; + } - if (written < 0 || (gsize)written != len) { - LOG(ERROR) << "Could not write file " << error_state_path.value(); + int written = base::WriteFile(error_state_path, + decoded_error_state.c_str(), + decoded_error_state.length()); + if (written < 0 || + static_cast(written) != decoded_error_state.length()) { + LOG(ERROR) << "Could not write file " << error_state_path.value() + << " Written: " << written << " Len: " + << decoded_error_state.length(); base::DeleteFile(error_state_path, false); return false; } @@ -208,6 +196,13 @@ bool ChromeCollector::HandleCrash(const FilePath &file_path, return true; } +void ChromeCollector::SetUpDBus() { + CrashCollector::SetUpDBus(); + + debugd_proxy_.reset( + new org::chromium::debugdProxy(bus_, debugd::kDebugdServiceName)); +} + bool ChromeCollector::ParseCrashLog(const std::string &data, const FilePath &dir, const FilePath &minidump, @@ -325,11 +320,14 @@ std::map ChromeCollector::GetAdditionalLogs( base::DeleteFile(chrome_log_path, false /* recursive */); } - // Now get the GPU state from debugd. - const FilePath dri_error_state_path = - GetCrashPath(dir, basename, kGpuStateFilename); - if (GetDriErrorState(dri_error_state_path)) - logs[kGpuStateFilename] = dri_error_state_path; + // For unit testing, debugd_proxy_ isn't initialized, so skip attempting to + // get the GPU error state from debugd. + if (debugd_proxy_) { + const FilePath dri_error_state_path = + GetCrashPath(dir, basename, kGpuStateFilename); + if (GetDriErrorState(dri_error_state_path, debugd_proxy_.get())) + logs[kGpuStateFilename] = dri_error_state_path; + } return logs; } diff --git a/crash_reporter/chrome_collector.h b/crash_reporter/chrome_collector.h index 566b5a8a9..0b58c1948 100644 --- a/crash_reporter/chrome_collector.h +++ b/crash_reporter/chrome_collector.h @@ -5,6 +5,7 @@ #ifndef CRASH_REPORTER_CHROME_COLLECTOR_H_ #define CRASH_REPORTER_CHROME_COLLECTOR_H_ +#include #include #include @@ -12,6 +13,7 @@ #include // for FRIEND_TEST #include "crash-reporter/crash_collector.h" +#include "debugd/dbus-proxies.h" class SystemLogging; @@ -30,6 +32,9 @@ class ChromeCollector : public CrashCollector { const std::string &uid_string, const std::string &exe_name); + protected: + void SetUpDBus() override; + private: friend class ChromeCollectorTest; FRIEND_TEST(ChromeCollectorTest, GoodValues); @@ -58,6 +63,9 @@ class ChromeCollector : public CrashCollector { FILE *output_file_ptr_; + // D-Bus proxy for debugd interface. Unset in unit tests. + std::unique_ptr debugd_proxy_; + DISALLOW_COPY_AND_ASSIGN(ChromeCollector); }; diff --git a/crash_reporter/chrome_collector_test.cc b/crash_reporter/chrome_collector_test.cc index d44c82c50..0d6a7cea4 100644 --- a/crash_reporter/chrome_collector_test.cc +++ b/crash_reporter/chrome_collector_test.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include using base::FilePath; @@ -40,6 +41,11 @@ bool IsMetrics() { } // namespace +class ChromeCollectorMock : public ChromeCollector { + public: + MOCK_METHOD0(SetUpDBus, void()); +}; + class ChromeCollectorTest : public ::testing::Test { protected: void ExpectFileEquals(const char *golden, @@ -49,10 +55,12 @@ class ChromeCollectorTest : public ::testing::Test { EXPECT_EQ(golden, contents); } - ChromeCollector collector_; + ChromeCollectorMock collector_; private: void SetUp() override { + EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return()); + collector_.Initialize(CountCrash, IsMetrics); chromeos::ClearLog(); } diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index b42657b6e..6e1ceac00 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -39,6 +39,28 @@ 'unclean_shutdown_collector.cc', 'user_collector.cc', ], + 'actions': [ + { + 'action_name': 'generate-session-manager-proxies', + 'variables': { + 'proxy_output_file': 'include/session_manager/dbus-proxies.h' + }, + 'sources': [ + '../login_manager/dbus_bindings/org.chromium.SessionManager.xml', + ], + 'includes': ['../common-mk/generate-dbus-proxies.gypi'], + }, + { + 'action_name': 'generate-debugd-proxies', + 'variables': { + 'proxy_output_file': 'include/debugd/dbus-proxies.h' + }, + 'sources': [ + '../debugd/share/org.chromium.debugd.xml', + ], + 'includes': ['../common-mk/generate-dbus-proxies.gypi'], + }, + ], }, { 'target_name': 'crash_reporter', diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index a53bff9f4..d67356d9b 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -18,9 +18,6 @@ #include #include -#include -#include - #include #include #include @@ -92,6 +89,8 @@ CrashCollector::CrashCollector() } CrashCollector::~CrashCollector() { + if (bus_) + bus_->ShutdownAndBlock(); } void CrashCollector::Initialize( @@ -102,6 +101,21 @@ void CrashCollector::Initialize( count_crash_function_ = count_crash_function; is_feedback_allowed_function_ = is_feedback_allowed_function; + + SetUpDBus(); +} + +void CrashCollector::SetUpDBus() { + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SYSTEM; + + bus_ = new dbus::Bus(options); + CHECK(bus_->Connect()); + + session_manager_proxy_.reset( + new org::chromium::SessionManagerInterfaceProxy( + bus_, + login_manager::kSessionManagerInterface)); } int CrashCollector::WriteNewFile(const FilePath &filename, @@ -154,49 +168,19 @@ FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory, extension.c_str())); } -namespace { +bool CrashCollector::GetActiveUserSessions( + std::map *sessions) { + chromeos::ErrorPtr error; + session_manager_proxy_->RetrieveActiveSessions(sessions, &error); -const char *GetGErrorMessage(const GError *error) { - if (!error) - return "Unknown error."; - return error->message; -} - -} - -GHashTable *CrashCollector::GetActiveUserSessions() { - GHashTable *active_sessions = nullptr; - - chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); - if (!dbus.HasConnection()) { - LOG(ERROR) << "Error connecting to system D-Bus"; - return active_sessions; - } - chromeos::dbus::Proxy proxy(dbus, - login_manager::kSessionManagerServiceName, - login_manager::kSessionManagerServicePath, - login_manager::kSessionManagerInterface); - if (!proxy) { - LOG(ERROR) << "Error creating D-Bus proxy to interface " - << "'" << login_manager::kSessionManagerServiceName << "'"; - return active_sessions; + if (error) { + LOG(ERROR) << "Error calling D-Bus proxy call to interface " + << "'" << session_manager_proxy_->GetObjectPath().value() << "':" + << error->GetMessage(); + return false; } - // Request all the active sessions. - GError *gerror = nullptr; - if (!dbus_g_proxy_call(proxy.gproxy(), - login_manager::kSessionManagerRetrieveActiveSessions, - &gerror, G_TYPE_INVALID, - DBUS_TYPE_G_STRING_STRING_HASHTABLE, &active_sessions, - G_TYPE_INVALID)) { - LOG(ERROR) << "Error performing D-Bus proxy call " - << "'" - << login_manager::kSessionManagerRetrieveActiveSessions << "'" - << ": " << GetGErrorMessage(gerror); - return active_sessions; - } - - return active_sessions; + return true; } FilePath CrashCollector::GetUserCrashPath() { @@ -204,19 +188,14 @@ FilePath CrashCollector::GetUserCrashPath() { // Ask the session manager for the active ones, then just run with the // first result we get back. FilePath user_path = FilePath(kFallbackUserCrashPath); - GHashTable *active_sessions = GetActiveUserSessions(); - if (!active_sessions) + std::map active_sessions; + if (!GetActiveUserSessions(&active_sessions)) { + LOG(ERROR) << "Could not get active user sessions, using default."; return user_path; - - GList *list = g_hash_table_get_values(active_sessions); - if (list) { - const char *salted_path = static_cast(list->data); - user_path = chromeos::cryptohome::home::GetHashedUserPath(salted_path) - .Append("crash"); - g_list_free(list); } - g_hash_table_destroy(active_sessions); + user_path = chromeos::cryptohome::home::GetHashedUserPath( + active_sessions.begin()->second).Append("crash"); return user_path; } diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h index 68806d9ed..ef443d3e3 100644 --- a/crash_reporter/crash_collector.h +++ b/crash_reporter/crash_collector.h @@ -10,12 +10,13 @@ #include #include -#include - #include #include +#include #include // for FRIEND_TEST +#include "session_manager/dbus-proxies.h" + // User crash collector. class CrashCollector { public: @@ -60,6 +61,9 @@ class CrashCollector { // Set maximum enqueued crashes in a crash directory. static const int kMaxCrashDirectorySize; + // Set up D-Bus. + virtual void SetUpDBus(); + // Writes |data| of |size| to |filename|, which must be a new file. // If the file already exists or writing fails, return a negative value. // Otherwise returns the number of bytes written. @@ -75,7 +79,8 @@ class CrashCollector { forced_crash_directory_ = forced_directory; } - virtual GHashTable *GetActiveUserSessions(); + virtual bool GetActiveUserSessions( + std::map *sessions); base::FilePath GetUserCrashPath(); base::FilePath GetCrashDirectoryInfo(uid_t process_euid, uid_t default_user_id, @@ -161,7 +166,13 @@ class CrashCollector { std::string lsb_release_; base::FilePath log_config_path_; + scoped_refptr bus_; + private: + // D-Bus proxy for session manager interface. + std::unique_ptr + session_manager_proxy_; + DISALLOW_COPY_AND_ASSIGN(CrashCollector); }; diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc index 0ca379262..ce9af2b75 100644 --- a/crash_reporter/crash_collector_test.cc +++ b/crash_reporter/crash_collector_test.cc @@ -5,8 +5,7 @@ #include "crash-reporter/crash_collector_test.h" #include - -#include +#include #include #include @@ -19,6 +18,7 @@ using base::FilePath; using base::StringPrintf; using chromeos::FindLog; +using ::testing::Invoke; using ::testing::Return; namespace { @@ -32,11 +32,20 @@ bool IsMetrics() { return false; } +bool GetActiveUserSessionsImpl(std::map *sessions) { + char kUser[] = "chicken@butt.com"; + char kHash[] = "hashcakes"; + sessions->insert(std::pair(kUser, kHash)); + return true; +} + } // namespace class CrashCollectorTest : public ::testing::Test { public: void SetUp() { + EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(Return()); + collector_.Initialize(CountCrash, IsMetrics); test_dir_ = FilePath("test"); base::CreateDirectory(test_dir_); @@ -117,15 +126,8 @@ TEST_F(CrashCollectorTest, GetCrashDirectoryInfo) { EXPECT_EQ(kRootUid, directory_owner); EXPECT_EQ(kRootGid, directory_group); - // No need to destroy the hash as GetCrashDirectoryInfo() will do it for us. - GHashTable *active_sessions = g_hash_table_new(g_str_hash, g_str_equal); - char kUser[] = "chicken@butt.com"; - char kHash[] = "hashcakes"; - g_hash_table_insert(active_sessions, - static_cast(kUser), - static_cast(kHash)); - EXPECT_CALL(collector_, GetActiveUserSessions()) - .WillOnce(Return(active_sessions)); + EXPECT_CALL(collector_, GetActiveUserSessions(testing::_)) + .WillOnce(Invoke(&GetActiveUserSessionsImpl)); EXPECT_EQ(collector_.IsUserSpecificDirectoryEnabled(), true); diff --git a/crash_reporter/crash_collector_test.h b/crash_reporter/crash_collector_test.h index 28811b04e..8339fa056 100644 --- a/crash_reporter/crash_collector_test.h +++ b/crash_reporter/crash_collector_test.h @@ -7,14 +7,17 @@ #include "crash-reporter/crash_collector.h" -#include +#include +#include #include #include class CrashCollectorMock : public CrashCollector { public: - MOCK_METHOD0(GetActiveUserSessions, GHashTable*()); + MOCK_METHOD0(SetUpDBus, void()); + MOCK_METHOD1(GetActiveUserSessions, + bool(std::map *sessions)); }; #endif // CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_ diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc index c5824aa84..1528b3f15 100644 --- a/crash_reporter/crash_reporter.cc +++ b/crash_reporter/crash_reporter.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include // for open -#include #include #include @@ -275,8 +274,6 @@ int main(int argc, char *argv[]) { chromeos::OpenLog(my_path.BaseName().value().c_str(), true); chromeos::InitLog(chromeos::kLogToSyslog); - ::g_type_init(); - KernelCollector kernel_collector; kernel_collector.Initialize(CountKernelCrash, IsFeedbackAllowed); UserCollector user_collector; diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc index b067afd7b..48d94ea0e 100644 --- a/crash_reporter/kernel_collector_test.cc +++ b/crash_reporter/kernel_collector_test.cc @@ -52,6 +52,9 @@ class KernelCollectorTest : public ::testing::Test { void SetUp() override { s_crashes = 0; s_metrics = true; + + EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return()); + collector_.Initialize(CountCrash, IsMetrics); ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); test_kcrash_ = scoped_temp_dir_.path().Append("kcrash"); diff --git a/crash_reporter/kernel_collector_test.h b/crash_reporter/kernel_collector_test.h index 32193246a..75ac01e9b 100644 --- a/crash_reporter/kernel_collector_test.h +++ b/crash_reporter/kernel_collector_test.h @@ -13,6 +13,7 @@ class KernelCollectorMock : public KernelCollector { public: MOCK_METHOD0(DumpDirMounted, bool()); + MOCK_METHOD0(SetUpDBus, void()); }; #endif // CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_ diff --git a/crash_reporter/testrunner.cc b/crash_reporter/testrunner.cc index 24e2329c1..d45bbf875 100644 --- a/crash_reporter/testrunner.cc +++ b/crash_reporter/testrunner.cc @@ -3,11 +3,9 @@ // found in the LICENSE file. #include -#include #include int main(int argc, char** argv) { - ::g_type_init(); SetUpTests(&argc, argv, true); return RUN_ALL_TESTS(); } diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc index f41b06f22..1705a6d93 100644 --- a/crash_reporter/udev_collector_test.cc +++ b/crash_reporter/udev_collector_test.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "crash-reporter/udev_collector.h" @@ -46,6 +47,11 @@ int GetNumLogFiles(const FilePath& path) { } // namespace +class UdevCollectorMock : public UdevCollector { + public: + MOCK_METHOD0(SetUpDBus, void()); +}; + class UdevCollectorTest : public ::testing::Test { protected: base::ScopedTempDir temp_dir_generator_; @@ -58,6 +64,8 @@ class UdevCollectorTest : public ::testing::Test { void SetUp() override { s_consent_given = true; + EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return()); + collector_.Initialize(CountCrash, IsMetrics); ASSERT_TRUE(temp_dir_generator_.CreateUniqueTempDir()); @@ -76,7 +84,7 @@ class UdevCollectorTest : public ::testing::Test { chromeos::ClearLog(); } - UdevCollector collector_; + UdevCollectorMock collector_; }; TEST_F(UdevCollectorTest, TestNoConsent) { diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc index 859b88144..f5e1b32bf 100644 --- a/crash_reporter/unclean_shutdown_collector_test.cc +++ b/crash_reporter/unclean_shutdown_collector_test.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include using base::FilePath; @@ -33,9 +34,17 @@ bool IsMetrics() { } // namespace +class UncleanShutdownCollectorMock : public UncleanShutdownCollector { + public: + MOCK_METHOD0(SetUpDBus, void()); +}; + class UncleanShutdownCollectorTest : public ::testing::Test { void SetUp() { s_crashes = 0; + + EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return()); + collector_.Initialize(CountCrash, IsMetrics); rmdir(kTestDirectory); @@ -46,13 +55,14 @@ class UncleanShutdownCollectorTest : public ::testing::Test { collector_.powerd_suspended_file_ = FilePath(kTestSuspended); chromeos::ClearLog(); } + protected: void WriteStringToFile(const FilePath &file_path, const char *data) { ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data))); } - UncleanShutdownCollector collector_; + UncleanShutdownCollectorMock collector_; FilePath test_unclean_; }; diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc index 5a5bb0cd4..823d8b4e1 100644 --- a/crash_reporter/user_collector_test.cc +++ b/crash_reporter/user_collector_test.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include using base::FilePath; @@ -39,9 +40,17 @@ bool IsMetrics() { } // namespace +class UserCollectorMock : public UserCollector { + public: + MOCK_METHOD0(SetUpDBus, void()); +}; + class UserCollectorTest : public ::testing::Test { void SetUp() { s_crashes = 0; + + EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return()); + collector_.Initialize(CountCrash, kFilePath, IsMetrics, @@ -71,7 +80,7 @@ class UserCollectorTest : public ::testing::Test { return result; } - UserCollector collector_; + UserCollectorMock collector_; pid_t pid_; }; From f5767af079161b0d42a2a6fdbb54690f8384f798 Mon Sep 17 00:00:00 2001 From: Steve Fung Date: Thu, 12 Feb 2015 23:31:20 -0800 Subject: [PATCH 217/225] crash: Fix session manager D-Bus call service name The D-Bus call to session manager was using the incorrect service name field, causing the crash reporter session lookup to fail and fallback to placing the crash in /home/chronos/crash. Fix the D-Bus call so that crashes are put in the right place. Also, fixing this exposed an error with processing the results. The previous D-Bus code treated an empty session response the same as a D-Bus connection error, with the new code D-Bus errors are handled separately, so also check whether the returned array is empty. BUG=chromium:458330 TEST=triggered crash with 'chrome://crash' page; crash files are placed in /home/chronos/user/crash TEST=`test_that -b panther logging_UserCrash` passes Change-Id: I77733adc5e4287d9eae83a749818d9cc38398c80 Reviewed-on: https://chromium-review.googlesource.com/249800 Reviewed-by: Dan Erat Tested-by: Steve Fung Commit-Queue: Steve Fung --- crash_reporter/crash_collector.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index d67356d9b..b238257ed 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -115,7 +115,7 @@ void CrashCollector::SetUpDBus() { session_manager_proxy_.reset( new org::chromium::SessionManagerInterfaceProxy( bus_, - login_manager::kSessionManagerInterface)); + login_manager::kSessionManagerServiceName)); } int CrashCollector::WriteNewFile(const FilePath &filename, @@ -189,7 +189,7 @@ FilePath CrashCollector::GetUserCrashPath() { // first result we get back. FilePath user_path = FilePath(kFallbackUserCrashPath); std::map active_sessions; - if (!GetActiveUserSessions(&active_sessions)) { + if (!GetActiveUserSessions(&active_sessions) || active_sessions.empty()) { LOG(ERROR) << "Could not get active user sessions, using default."; return user_path; } From 040dffdb3e9e3a459ad137b02325e455b4b63c8f Mon Sep 17 00:00:00 2001 From: Steve Fung Date: Tue, 10 Feb 2015 14:25:00 -0800 Subject: [PATCH 218/225] crash: Convert list_proxies to chromeos-dbus-bindings generator Switch list_proxies to using chromeos-dbus-bindings generator in order to remove the glib dependency. BUG=brillo:89 TEST=`FEATURES=test emerge-panther crash-reporter` TEST=manually tested `list_proxies --quiet \ https://clieents2.google.com/cr/report` Change-Id: Ic52277b2e14514376f4d55e627e1651d9ef566c5 Reviewed-on: https://chromium-review.googlesource.com/248781 Reviewed-by: Dan Erat Trybot-Ready: Dan Erat Commit-Queue: Steve Fung Tested-by: Steve Fung --- crash_reporter/crash-reporter.gyp | 17 +- .../org.chromium.LibCrosService.xml | 20 ++ crash_reporter/list_proxies.cc | 278 ++++++++++-------- 3 files changed, 188 insertions(+), 127 deletions(-) create mode 100644 crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index 6e1ceac00..988cb5d4d 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -6,7 +6,6 @@ 'variables': { 'deps': [ 'libchromeos-<(libbase_ver)', - 'dbus-glib-1', ], }, }, @@ -16,8 +15,6 @@ 'type': 'static_library', 'variables': { 'exported_deps': [ - 'glib-2.0', - 'gobject-2.0', 'libchrome-<(libbase_ver)', 'libpcrecpp', ], @@ -68,7 +65,6 @@ 'variables': { 'deps': [ 'dbus-1', - 'dbus-glib-1', 'libmetrics-<(libbase_ver)', ], }, @@ -85,13 +81,24 @@ 'variables': { 'deps': [ 'dbus-1', - 'dbus-glib-1', 'libchrome-<(libbase_ver)', ], }, 'sources': [ 'list_proxies.cc', ], + 'actions': [ + { + 'action_name': 'generate-lib-cros-service-proxies', + 'variables': { + 'proxy_output_file': 'include/libcrosservice/dbus-proxies.h' + }, + 'sources': [ + './dbus_bindings/org.chromium.LibCrosService.xml', + ], + 'includes': ['../common-mk/generate-dbus-proxies.gypi'], + }, + ], }, { 'target_name': 'warn_collector', diff --git a/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml b/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml new file mode 100644 index 000000000..64b8b84fd --- /dev/null +++ b/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc index 6cac8f8f4..de02f0ac4 100644 --- a/crash_reporter/list_proxies.cc +++ b/crash_reporter/list_proxies.cc @@ -2,35 +2,36 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include -#include +#include #include // for isatty() -#include #include +#include +#include #include #include +#include #include #include #include #include -#include +#include #include -const char kLibCrosProxyResolveSignalInterface[] = +#include "libcrosservice/dbus-proxies.h" + +using std::unique_ptr; + +namespace { + +const char kLibCrosProxyResolvedSignalInterface[] = "org.chromium.CrashReporterLibcrosProxyResolvedInterface"; -const char kLibCrosProxyResolveName[] = "ProxyResolved"; -const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface"; +const char kLibCrosProxyResolvedName[] = "ProxyResolved"; const char kLibCrosServiceName[] = "org.chromium.LibCrosService"; -const char kLibCrosServicePath[] = "/org/chromium/LibCrosService"; -const char kLibCrosServiceResolveNetworkProxyMethodName[] = - "ResolveNetworkProxy"; const char kNoProxy[] = "direct://"; -namespace switches { - -const unsigned kTimeoutDefault = 5; +const int kTimeoutDefaultSeconds = 5; const char kHelp[] = "help"; const char kQuiet[] = "quiet"; @@ -46,19 +47,11 @@ const char kHelpMessage[] = " --timeout=N Set timeout for browser resolving proxies (default is 5)\n" " --help Show this help.\n"; -} // namespace switches - -static const char *GetGErrorMessage(const GError *error) { - if (!error) - return "Unknown error."; - return error->message; -} - // Copied from src/update_engine/chrome_browser_proxy_resolver.cc // Parses the browser's answer for resolved proxies. It returns a // list of strings, each of which is a resolved proxy. -std::deque ParseProxyString(const std::string &input) { - std::deque ret; +std::vector ParseProxyString(const std::string& input) { + std::vector ret; // Some of this code taken from // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc @@ -99,130 +92,173 @@ std::deque ParseProxyString(const std::string &input) { return ret; } -// Define a signal-watcher class to handle the D-Bus signal sent to us when -// the browser answers our request to resolve proxies. -class BrowserProxyResolvedSignalWatcher : public chromeos::dbus::SignalWatcher { +// A class for interfacing with Chrome to resolve proxies for a given source +// url. The class is initialized with the given source url to check, the +// signal interface and name that Chrome will reply to, and how long to wait +// for the resolve request to timeout. Once initialized, the Run() function +// must be called, which blocks on the D-Bus call to Chrome. The call returns +// after either the timeout or the proxy has been resolved. The resolved +// proxies can then be accessed through the proxies() function. +class ProxyResolver : public chromeos::DBusDaemon { public: - explicit BrowserProxyResolvedSignalWatcher(GMainLoop *main_loop, - std::deque *proxies) - : main_loop_(main_loop), proxies_(proxies) { } + ProxyResolver(const std::string& source_url, + const std::string& signal_interface, + const std::string& signal_name, + base::TimeDelta timeout) + : source_url_(source_url), + signal_interface_(signal_interface), + signal_name_(signal_name), + timeout_(timeout), + weak_ptr_factory_(this), + timeout_callback_(base::Bind(&ProxyResolver::HandleBrowserTimeout, + weak_ptr_factory_.GetWeakPtr())) {} - void OnSignal(DBusMessage *message) override { - // Get args - char *source_url = nullptr; - char *proxy_list = nullptr; - char *error = nullptr; - DBusError arg_error; - dbus_error_init(&arg_error); - if (!dbus_message_get_args(message, &arg_error, - DBUS_TYPE_STRING, &source_url, - DBUS_TYPE_STRING, &proxy_list, - DBUS_TYPE_STRING, &error, - DBUS_TYPE_INVALID)) { - LOG(ERROR) << "Error reading D-Bus signal"; - return; - } - if (!source_url || !proxy_list) { - LOG(ERROR) << "Error getting url, proxy list from D-Bus signal"; + ~ProxyResolver() override {} + + const std::vector& proxies() { + return proxies_; + } + + int Run() override { + // Add task for if the browser proxy call times out. + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + timeout_callback_.callback(), + timeout_); + + return chromeos::DBusDaemon::Run(); + } + + protected: + // If the browser times out, quit the run loop. + void HandleBrowserTimeout() { + LOG(ERROR) << "Timeout while waiting for browser to resolve proxy"; + Quit(); + } + + // If the signal handler connects successfully, call the browser's + // ResolveNetworkProxy D-Bus method. Otherwise, don't do anything and let + // the timeout task quit the run loop. + void HandleDBusSignalConnected(const std::string& interface, + const std::string& signal, + bool success) { + if (!success) { + LOG(ERROR) << "Could not connect to signal " << interface << "." + << signal; + timeout_callback_.Cancel(); + Quit(); return; } - const std::deque &proxies = ParseProxyString(proxy_list); - for (std::deque::const_iterator it = proxies.begin(); - it != proxies.end(); ++it) { - LOG(INFO) << "Found proxy via browser signal: " << (*it).c_str(); - proxies_->push_back(*it); - } + chromeos::ErrorPtr error; + call_proxy_->ResolveNetworkProxy(source_url_, + signal_interface_, + signal_name_, + &error); - g_main_loop_quit(main_loop_); + if (error) { + LOG(ERROR) << "Call to ResolveNetworkProxy failed: " + << error->GetMessage(); + timeout_callback_.Cancel(); + Quit(); + } + } + + // Handle incoming ProxyResolved signal. + void HandleProxyResolvedSignal(const std::string& source_url, + const std::string& proxy_info, + const std::string& error_message) { + timeout_callback_.Cancel(); + proxies_ = ParseProxyString(proxy_info); + LOG(INFO) << "Found proxies via browser signal: " + << JoinString(proxies_, 'x'); + + Quit(); + } + + int OnInit() override { + int return_code = chromeos::DBusDaemon::OnInit(); + if (return_code != EX_OK) + return return_code; + + // Initialize D-Bus proxies. + call_proxy_.reset( + new org::chromium::LibCrosServiceInterfaceProxy(bus_, + kLibCrosServiceName)); + signal_proxy_.reset( + new org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy( + bus_, + kLibCrosServiceName)); + + // Set up the D-Bus signal handler. + // TODO(crbug.com/446115): Update ResolveNetworkProxy call to use an + // asynchronous return value rather than a return signal. + signal_proxy_->RegisterProxyResolvedSignalHandler( + base::Bind(&ProxyResolver::HandleProxyResolvedSignal, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&ProxyResolver::HandleDBusSignalConnected, + weak_ptr_factory_.GetWeakPtr())); + + return EX_OK; } private: - GMainLoop *main_loop_; - std::deque *proxies_; + unique_ptr call_proxy_; + unique_ptr + signal_proxy_; + + const std::string source_url_; + const std::string signal_interface_; + const std::string signal_name_; + base::TimeDelta timeout_; + + std::vector proxies_; + base::WeakPtrFactory weak_ptr_factory_; + + base::CancelableClosure timeout_callback_; + + DISALLOW_COPY_AND_ASSIGN(ProxyResolver); }; -static gboolean HandleBrowserTimeout(void *data) { - GMainLoop *main_loop = reinterpret_cast(data); - LOG(ERROR) << "Timeout while waiting for browser to resolve proxy"; - g_main_loop_quit(main_loop); - return false; // only call once -} +static bool ShowBrowserProxies(std::string url, base::TimeDelta timeout) { + // Initialize and run the proxy resolver to watch for signals. + ProxyResolver resolver(url, + kLibCrosProxyResolvedSignalInterface, + kLibCrosProxyResolvedName, + timeout); + resolver.Run(); -static bool ShowBrowserProxies(std::string url, unsigned timeout) { - GMainLoop *main_loop = g_main_loop_new(nullptr, false); + std::vector proxies = resolver.proxies(); - chromeos::dbus::BusConnection dbus = chromeos::dbus::GetSystemBusConnection(); - if (!dbus.HasConnection()) { - LOG(ERROR) << "Error connecting to system D-Bus"; - return false; - } - chromeos::dbus::Proxy browser_proxy(dbus, - kLibCrosServiceName, - kLibCrosServicePath, - kLibCrosServiceInterface); - if (!browser_proxy) { - LOG(ERROR) << "Error creating D-Bus proxy to interface " - << "'" << kLibCrosServiceName << "'"; - return false; - } - - // Watch for a proxy-resolved signal sent to us - std::deque proxies; - BrowserProxyResolvedSignalWatcher proxy_resolver(main_loop, &proxies); - proxy_resolver.StartMonitoring(kLibCrosProxyResolveSignalInterface, - kLibCrosProxyResolveName); - - // Request the proxies for our URL. The answer is sent to us via a - // proxy-resolved signal. - GError *gerror = nullptr; - if (!dbus_g_proxy_call(browser_proxy.gproxy(), - kLibCrosServiceResolveNetworkProxyMethodName, - &gerror, - G_TYPE_STRING, url.c_str(), - G_TYPE_STRING, kLibCrosProxyResolveSignalInterface, - G_TYPE_STRING, kLibCrosProxyResolveName, - G_TYPE_INVALID, G_TYPE_INVALID)) { - LOG(ERROR) << "Error performing D-Bus proxy call " - << "'" << kLibCrosServiceResolveNetworkProxyMethodName << "'" - << ": " << GetGErrorMessage(gerror); - return false; - } - - // Setup a timeout in case the browser doesn't respond with our signal - g_timeout_add_seconds(timeout, &HandleBrowserTimeout, main_loop); - - // Loop until we either get the proxy-resolved signal, or until the - // timeout is reached. - g_main_loop_run(main_loop); - - // If there are no proxies, then we failed to get the proxy-resolved - // signal (e.g. timeout was reached). + // If proxies is empty, then the timeout was reached waiting for the proxy + // resolved signal. If no proxies are defined, proxies will be populated + // with "direct://". if (proxies.empty()) return false; - for (std::deque::const_iterator it = proxies.begin(); - it != proxies.end(); ++it) { - printf("%s\n", (*it).c_str()); + for (const auto& proxy : proxies) { + printf("%s\n", proxy.c_str()); } return true; } +} // namespace + int main(int argc, char *argv[]) { CommandLine::Init(argc, argv); CommandLine* cl = CommandLine::ForCurrentProcess(); - if (cl->HasSwitch(switches::kHelp)) { - LOG(INFO) << switches::kHelpMessage; + if (cl->HasSwitch(kHelp)) { + LOG(INFO) << kHelpMessage; return 0; } - bool quiet = cl->HasSwitch(switches::kQuiet); - bool verbose = cl->HasSwitch(switches::kVerbose); + bool quiet = cl->HasSwitch(kQuiet); + bool verbose = cl->HasSwitch(kVerbose); - unsigned timeout = switches::kTimeoutDefault; - std::string str_timeout = cl->GetSwitchValueASCII(switches::kTimeout); - if (!str_timeout.empty() && !base::StringToUint(str_timeout, &timeout)) { + int timeout = kTimeoutDefaultSeconds; + std::string str_timeout = cl->GetSwitchValueASCII(kTimeout); + if (!str_timeout.empty() && !base::StringToInt(str_timeout, &timeout)) { LOG(ERROR) << "Invalid timeout value: " << str_timeout; return 1; } @@ -236,8 +272,6 @@ int main(int argc, char *argv[]) { init_flags |= chromeos::kLogToStderr; chromeos::InitLog(init_flags); - ::g_type_init(); - std::string url; CommandLine::StringVector urls = cl->GetArgs(); if (!urls.empty()) { @@ -247,7 +281,7 @@ int main(int argc, char *argv[]) { LOG(INFO) << "Resolving proxies without URL"; } - if (!ShowBrowserProxies(url, timeout)) { + if (!ShowBrowserProxies(url, base::TimeDelta::FromSeconds(timeout))) { LOG(ERROR) << "Error resolving proxies via the browser"; LOG(INFO) << "Assuming direct proxy"; printf("%s\n", kNoProxy); From 7458089196cba04f2fccd14932aa2b402f924143 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 27 Mar 2015 17:07:20 -0400 Subject: [PATCH 219/225] delete __STDC_{FORMAT,LIMIT}_MACROS With newer glibc versions (2.18+), these macros no longer exist (and the functionality they protected are always enabled). Delete them. BUG=chromium:401360 TEST=precq passes Change-Id: I21b0607a874b9f9e39dff050054a2928a6c55023 Reviewed-on: https://chromium-review.googlesource.com/262903 Trybot-Ready: Mike Frysinger Reviewed-by: Alex Vakulenko Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- crash_reporter/crash_collector.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index b238257ed..f970d1dfe 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -6,7 +6,6 @@ #include #include // For file creation modes. -#define __STDC_FORMAT_MACROS // PRId64 #include #include // PATH_MAX #include // For struct passwd. From 6aa551ec416bac9ec96a6103e25be78221cf36a4 Mon Sep 17 00:00:00 2001 From: Peter Qiu Date: Fri, 6 Mar 2015 11:42:04 -0800 Subject: [PATCH 220/225] crash-reporter: add support for device coredump Copy the device coredump file to /var/spool/crash directory when udev event "devcoredump" is detected. Also include the last 50 lines of /var/log/messages and /var/log/net.log in the coredump file for debugging purpose. Only perform the collection if feedback is allowed (metrics are enabled) or the device is running a developer image. By default, crash-reporter will not upload device coredumps to the crash server. The user can enable device coredumps upload via a to-be-added crosh command, which should only be done at the direction of CrOS engineers. BUG=chromium:464872 TEST=USE="asan clang" FEATURES=test emerge-$BOARD crash-reporter Manual Test: 1. Trigger wifi firmware error on a Clapper running developer image by running following command: "echo 1 > /sys/kernel/debug/iwlwifi/0000\:01\:00.0/iwlmvm/fw_restart" 2. Verify there is a device coredump file in "/var/spool/crash/" "devcoredump_iwlwifi*.devcore", as well as the additional log file ".log" and the meta file ".meta". Change-Id: Ic4cf67d4b5715a6f422505f409276d1261b7d51f Reviewed-on: https://chromium-review.googlesource.com/257091 Reviewed-by: Zeping Qiu Commit-Queue: Zeping Qiu Tested-by: Zeping Qiu Reviewed-by: Mike Frysinger --- crash_reporter/99-crash-reporter.rules | 1 + crash_reporter/crash_reporter_logs.conf | 9 ++ crash_reporter/crash_sender | 19 ++- crash_reporter/udev_collector.cc | 182 +++++++++++++++++++++--- crash_reporter/udev_collector.h | 24 ++++ crash_reporter/udev_collector_test.cc | 73 +++++++++- 6 files changed, 278 insertions(+), 30 deletions(-) diff --git a/crash_reporter/99-crash-reporter.rules b/crash_reporter/99-crash-reporter.rules index 57a71cac5..aea5b1c36 100644 --- a/crash_reporter/99-crash-reporter.rules +++ b/crash_reporter/99-crash-reporter.rules @@ -3,3 +3,4 @@ ACTION=="change", SUBSYSTEM=="drm", KERNEL=="card0", ENV{ERROR}=="1", RUN+="/sbi ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="cyapa", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-cyapa:ACTION=change" # For detecting Atmel trackpad/touchscreen issue. Passing into crash_reporter SUBSYSTEM=i2c-atmel_mxt_ts since crash_reporter does not handle DRIVER string. ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="atmel_mxt_ts", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-atmel_mxt_ts:ACTION=change" +ACTION=="add", SUBSYSTEM=="devcoredump", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=devcoredump:ACTION=add:KERNEL_NUMBER=%n" diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf index 1a69032fb..f5ca80c91 100644 --- a/crash_reporter/crash_reporter_logs.conf +++ b/crash_reporter/crash_reporter_logs.conf @@ -74,6 +74,15 @@ crash_reporter-udev-collection---TouchNoise=cat /var/log/touch_noise.log # Periodically collect touch event log for debugging (crosbug.com/p/17244) crash_reporter-udev-collection---TouchEvent=cat /var/log/touch_event.log +# Collect the last 50 lines of /var/log/messages and /var/log/net.log for +# intel wifi driver (iwlwifi) for debugging purpose. +crash_reporter-udev-collection-devcoredump-iwlwifi=\ + echo "===/var/log/messages==="; \ + tail -n 50 /var/log/messages; \ + echo "===/var/log/net.log==="; \ + tail -n 50 /var/log/net.log; \ + echo EOF + # Dump the last 50 lines of the last two powerd log files -- if the job has # already restarted, we want to see the end of the previous instance's logs. powerd=\ diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index c83a12ae2..2ad1e4635 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -69,6 +69,10 @@ RUN_FILE="/var/run/crash_sender.pid" # Maximum time to sleep between sends. SECONDS_SEND_SPREAD=${SECONDS_SEND_SPREAD:-600} +# Set this to 1 to allow uploading of device coredumps. +DEVCOREDUMP_UPLOAD_FLAG_FILE=\ +"/var/lib/crash_reporter/device_coredump_upload_allowed" + # The syslog tag for all logging we emit. TAG="$(basename $0)[$$]" @@ -156,6 +160,12 @@ is_developer_mode() { crossystem "devsw_boot?1" # exit status will be accurate } +# Return 0 if the uploading of device coredumps is allowed. +is_device_coredump_upload_allowed() { + [ -f "${DEVCOREDUMP_UPLOAD_FLAG_FILE}" ] && return 0 + return 1 +} + # Generate a uniform random number in 0..max-1. generate_uniform_random() { local max=$1 @@ -508,7 +518,8 @@ send_crashes() { local kind=$(get_kind "${meta_path}") if [ "${kind}" != "minidump" ] && \ [ "${kind}" != "kcrash" ] && \ - [ "${kind}" != "log" ]; then + [ "${kind}" != "log" ] && + [ "${kind}" != "devcore" ]; then lecho "Unknown report kind ${kind}. Removing report." remove_report "${meta_path}" continue @@ -527,6 +538,12 @@ send_crashes() { continue fi + # Ignore device coredump if device coredump uploading is not allowed. + if [ "${kind}" = "devcore" ] && ! is_device_coredump_upload_allowed; then + lecho "Ignoring device coredump. Device coredump upload not allowed." + continue + fi + if ! is_mock && ! is_official_image; then lecho "Not an official OS version. Removing crash." remove_report "${meta_path}" diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc index 64ec3392c..908bbc9c4 100644 --- a/crash_reporter/udev_collector.cc +++ b/crash_reporter/udev_collector.cc @@ -8,26 +8,41 @@ #include #include +#include #include #include +#include #include #include +#include #include -static const char kCollectUdevSignature[] = "crash_reporter-udev-collection"; -static const char kGzipPath[] = "/bin/gzip"; -static const char kUdevExecName[] = "udev"; -static const char kUdevSignatureKey[] = "sig"; - using base::FilePath; -UdevCollector::UdevCollector() {} +namespace { + +const char kCollectUdevSignature[] = "crash_reporter-udev-collection"; +const char kGzipPath[] = "/bin/gzip"; +const char kUdevExecName[] = "udev"; +const char kUdevSignatureKey[] = "sig"; +const char kUdevSubsystemDevCoredump[] = "devcoredump"; +const char kDefaultDevCoredumpDirectory[] = "/sys/class/devcoredump"; +const char kDevCoredumpFilePrefixFormat[] = "devcoredump_%s"; + +} // namespace + +UdevCollector::UdevCollector() + : dev_coredump_directory_(kDefaultDevCoredumpDirectory) {} UdevCollector::~UdevCollector() {} bool UdevCollector::HandleCrash(const std::string &udev_event) { - if (!is_feedback_allowed_function_()) { - LOG(ERROR) << "No consent given to collect crash info."; + if (IsDeveloperImage()) { + LOG(INFO) << "developer image - collect udev crash info."; + } else if (is_feedback_allowed_function_()) { + LOG(INFO) << "Consent given - collect udev crash info."; + } else { + LOG(INFO) << "Ignoring - Non-developer image and no consent given."; return false; } @@ -43,24 +58,43 @@ bool UdevCollector::HandleCrash(const std::string &udev_event) { udev_event_map[iter->first] = iter->second; } - // Construct the basename string for crash_reporter_logs.conf: - // "crash_reporter-udev-collection-[action]-[name]-[subsystem]" - // If a udev field is not provided, "" is used in its place, e.g.: - // "crash_reporter-udev-collection-[action]--[subsystem]" - // Hence, "" is used as a wildcard name string. - // TODO(sque, crosbug.com/32238): Implement wildcard checking. - std::string basename = udev_event_map["ACTION"] + "-" + - udev_event_map["KERNEL"] + "-" + - udev_event_map["SUBSYSTEM"]; - std::string udev_log_name = std::string(kCollectUdevSignature) + '-' + - basename; - // Make sure the crash directory exists, or create it if it doesn't. FilePath crash_directory; if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, nullptr)) { LOG(ERROR) << "Could not get crash directory."; return false; } + + if (udev_event_map["SUBSYSTEM"] == kUdevSubsystemDevCoredump) { + int instance_number; + if (!base::StringToInt(udev_event_map["KERNEL_NUMBER"], &instance_number)) { + LOG(ERROR) << "Invalid kernel number: " + << udev_event_map["KERNEL_NUMBER"]; + return false; + } + return ProcessDevCoredump(crash_directory, instance_number); + } + + return ProcessUdevCrashLogs(crash_directory, + udev_event_map["ACTION"], + udev_event_map["KERNEL"], + udev_event_map["SUBSYSTEM"]); +} + +bool UdevCollector::ProcessUdevCrashLogs(const FilePath& crash_directory, + const std::string& action, + const std::string& kernel, + const std::string& subsystem) { + // Construct the basename string for crash_reporter_logs.conf: + // "crash_reporter-udev-collection-[action]-[name]-[subsystem]" + // If a udev field is not provided, "" is used in its place, e.g.: + // "crash_reporter-udev-collection-[action]--[subsystem]" + // Hence, "" is used as a wildcard name string. + // TODO(sque, crosbug.com/32238): Implement wildcard checking. + std::string basename = action + "-" + kernel + "-" + subsystem; + std::string udev_log_name = std::string(kCollectUdevSignature) + '-' + + basename; + // Create the destination path. std::string log_file_name = FormatDumpBasename(basename, time(nullptr), 0); @@ -85,10 +119,114 @@ bool UdevCollector::HandleCrash(const std::string &udev_event) { else crash_path = crash_path_zipped; - std::string exec_name = std::string(kUdevExecName) + "-" + - udev_event_map["SUBSYSTEM"]; + std::string exec_name = std::string(kUdevExecName) + "-" + subsystem; AddCrashMetaData(kUdevSignatureKey, udev_log_name); WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"), exec_name, crash_path.value()); return true; } + +bool UdevCollector::ProcessDevCoredump(const FilePath& crash_directory, + int instance_number) { + FilePath coredump_path = + FilePath(base::StringPrintf("%s/devcd%d/data", + dev_coredump_directory_.c_str(), + instance_number)); + if (!base::PathExists(coredump_path)) { + LOG(ERROR) << "Device coredump file " << coredump_path.value() + << " does not exist"; + return false; + } + + // Add coredump file to the crash directory. + if (!AppendDevCoredump(crash_directory, coredump_path, instance_number)) { + ClearDevCoredump(coredump_path); + return false; + } + + // Clear the coredump data to allow generation of future device coredumps + // without having to wait for the 5-minutes timeout. + return ClearDevCoredump(coredump_path); +} + +bool UdevCollector::AppendDevCoredump(const FilePath& crash_directory, + const FilePath& coredump_path, + int instance_number) { + // Retrieve the driver name of the failing device. + std::string driver_name = GetFailingDeviceDriverName(instance_number); + if (driver_name.empty()) { + LOG(ERROR) << "Failed to obtain driver name for instance: " + << instance_number; + return false; + } + + std::string coredump_prefix = + base::StringPrintf(kDevCoredumpFilePrefixFormat, driver_name.c_str()); + + std::string dump_basename = FormatDumpBasename(coredump_prefix, + time(nullptr), + instance_number); + FilePath core_path = GetCrashPath(crash_directory, dump_basename, "devcore"); + FilePath log_path = GetCrashPath(crash_directory, dump_basename, "log"); + FilePath meta_path = GetCrashPath(crash_directory, dump_basename, "meta"); + + // Collect coredump data. + if (!base::CopyFile(coredump_path, core_path)) { + LOG(ERROR) << "Failed to copy device coredumpm file from " + << coredump_path.value() << " to " << core_path.value(); + return false; + } + + // Collect additional logs if one is specified in the config file. + std::string udev_log_name = std::string(kCollectUdevSignature) + '-' + + kUdevSubsystemDevCoredump + '-' + driver_name; + bool result = GetLogContents(log_config_path_, udev_log_name, log_path); + if (result) { + AddCrashMetaUploadFile("logs", log_path.value()); + } + + WriteCrashMetaData(meta_path, coredump_prefix, core_path.value()); + + return true; +} + +bool UdevCollector::ClearDevCoredump(const FilePath& coredump_path) { + if (!base::WriteFile(coredump_path, "0", 1)) { + LOG(ERROR) << "Failed to delete the coredump data file " + << coredump_path.value(); + return false; + } + return true; +} + +std::string UdevCollector::GetFailingDeviceDriverName(int instance_number) { + FilePath failing_uevent_path = + FilePath(base::StringPrintf("%s/devcd%d/failing_device/uevent", + dev_coredump_directory_.c_str(), + instance_number)); + if (!base::PathExists(failing_uevent_path)) { + LOG(ERROR) << "Failing uevent path " << failing_uevent_path.value() + << " does not exist"; + return ""; + } + + std::string uevent_content; + if (!base::ReadFileToString(failing_uevent_path, &uevent_content)) { + LOG(ERROR) << "Failed to read uevent file " << failing_uevent_path.value(); + return ""; + } + + // Parse uevent file contents as key-value pairs. + std::vector> uevent_keyval; + base::SplitStringIntoKeyValuePairs(uevent_content, '=', '\n', &uevent_keyval); + std::vector>::const_iterator iter; + for (iter = uevent_keyval.begin(); + iter != uevent_keyval.end(); + ++iter) { + if (iter->first == "DRIVER") { + return iter->second; + } + } + + return ""; +} diff --git a/crash_reporter/udev_collector.h b/crash_reporter/udev_collector.h index 68eb85a4f..1689dd395 100644 --- a/crash_reporter/udev_collector.h +++ b/crash_reporter/udev_collector.h @@ -26,9 +26,33 @@ class UdevCollector : public CrashCollector { // could be omitted, in which case it would be treated as a wildcard (*). bool HandleCrash(const std::string& udev_event); + protected: + std::string dev_coredump_directory_; + private: friend class UdevCollectorTest; + // Process udev crash logs, collecting log files according to the config + // file (crash_reporter_logs.conf). + bool ProcessUdevCrashLogs(const base::FilePath& crash_directory, + const std::string& action, + const std::string& kernel, + const std::string& subsystem); + // Process device coredump, collecting device coredump file. + // |instance_number| is the kernel number of the virtual device for the device + // coredump instance. + bool ProcessDevCoredump(const base::FilePath& crash_directory, + int instance_number); + // Copy device coredump file to crash directory, and perform necessary + // coredump file management. + bool AppendDevCoredump(const base::FilePath& crash_directory, + const base::FilePath& coredump_path, + int instance_number); + // Clear the device coredump file by performing a dummy write to it. + bool ClearDevCoredump(const base::FilePath& coredump_path); + // Return the driver name of the device that generates the coredump. + std::string GetFailingDeviceDriverName(int instance_number); + // Mutator for unit testing. void set_log_config_path(const std::string& path) { log_config_path_ = base::FilePath(path); diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc index 1705a6d93..08d9b2c2f 100644 --- a/crash_reporter/udev_collector_test.cc +++ b/crash_reporter/udev_collector_test.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -18,12 +19,25 @@ namespace { // Dummy log config file name. const char kLogConfigFileName[] = "log_config_file"; +// Dummy directory for storing device coredumps. +const char kDevCoredumpDirectory[] = "devcoredump"; + // A bunch of random rules to put into the dummy log config file. const char kLogConfigFileContents[] = "crash_reporter-udev-collection-change-card0-drm=echo change card0 drm\n" "crash_reporter-udev-collection-add-state0-cpu=echo change state0 cpu\n" + "crash_reporter-udev-collection-devcoredump-iwlwifi=echo devcoredump\n" "cros_installer=echo not for udev"; +const char kCrashLogFilePattern[] = "*.log.gz"; +const char kDevCoredumpFilePattern[] = "*.devcore"; + +// Dummy content for device coredump data file. +const char kDevCoredumpDataContents[] = "coredump"; + +// Content for failing device's uevent file. +const char kFailingDeviceUeventContents[] = "DRIVER=iwlwifi\n"; + void CountCrash() {} bool s_consent_given = true; @@ -32,10 +46,11 @@ bool IsMetrics() { return s_consent_given; } -// Returns the number of compressed crash log files found in the given path. -int GetNumLogFiles(const FilePath& path) { +// Returns the number of files found in the given path that matches the +// specified file name pattern. +int GetNumFiles(const FilePath& path, const std::string& file_pattern) { base::FileEnumerator enumerator(path, false, base::FileEnumerator::FILES, - "*.log.gz"); + file_pattern); int num_files = 0; for (FilePath file_path = enumerator.Next(); !file_path.value().empty(); @@ -60,6 +75,35 @@ class UdevCollectorTest : public ::testing::Test { collector_.HandleCrash(udev_event); } + void GenerateDevCoredump(const std::string& device_name) { + // Generate coredump data file. + ASSERT_TRUE(CreateDirectory( + FilePath(base::StringPrintf("%s/%s", + collector_.dev_coredump_directory_.c_str(), + device_name.c_str())))); + FilePath data_path = + FilePath(base::StringPrintf("%s/%s/data", + collector_.dev_coredump_directory_.c_str(), + device_name.c_str())); + ASSERT_EQ(strlen(kDevCoredumpDataContents), + base::WriteFile(data_path, + kDevCoredumpDataContents, + strlen(kDevCoredumpDataContents))); + // Generate uevent file for failing device. + ASSERT_TRUE(CreateDirectory( + FilePath(base::StringPrintf("%s/%s/failing_device", + collector_.dev_coredump_directory_.c_str(), + device_name.c_str())))); + FilePath uevent_path = + FilePath(base::StringPrintf("%s/%s/failing_device/uevent", + collector_.dev_coredump_directory_.c_str(), + device_name.c_str())); + ASSERT_EQ(strlen(kFailingDeviceUeventContents), + base::WriteFile(uevent_path, + kFailingDeviceUeventContents, + strlen(kFailingDeviceUeventContents))); + } + private: void SetUp() override { s_consent_given = true; @@ -75,6 +119,10 @@ class UdevCollectorTest : public ::testing::Test { collector_.log_config_path_ = log_config_path; collector_.ForceCrashDirectory(temp_dir_generator_.path()); + FilePath dev_coredump_path = + temp_dir_generator_.path().Append(kDevCoredumpDirectory); + collector_.dev_coredump_directory_ = dev_coredump_path.value(); + // Write to a dummy log config file. ASSERT_EQ(strlen(kLogConfigFileContents), base::WriteFile(log_config_path, @@ -90,22 +138,33 @@ class UdevCollectorTest : public ::testing::Test { TEST_F(UdevCollectorTest, TestNoConsent) { s_consent_given = false; HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm"); - EXPECT_EQ(0, GetNumLogFiles(temp_dir_generator_.path())); + EXPECT_EQ(0, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern)); } TEST_F(UdevCollectorTest, TestNoMatch) { // No rule should match this. HandleCrash("ACTION=change:KERNEL=foo:SUBSYSTEM=bar"); - EXPECT_EQ(0, GetNumLogFiles(temp_dir_generator_.path())); + EXPECT_EQ(0, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern)); } TEST_F(UdevCollectorTest, TestMatches) { // Try multiple udev events in sequence. The number of log files generated // should increase. HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm"); - EXPECT_EQ(1, GetNumLogFiles(temp_dir_generator_.path())); + EXPECT_EQ(1, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern)); HandleCrash("ACTION=add:KERNEL=state0:SUBSYSTEM=cpu"); - EXPECT_EQ(2, GetNumLogFiles(temp_dir_generator_.path())); + EXPECT_EQ(2, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern)); +} + +TEST_F(UdevCollectorTest, TestDevCoredump) { + GenerateDevCoredump("devcd0"); + HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump"); + EXPECT_EQ(1, GetNumFiles(temp_dir_generator_.path(), + kDevCoredumpFilePattern)); + GenerateDevCoredump("devcd1"); + HandleCrash("ACTION=add:KERNEL_NUMBER=1:SUBSYSTEM=devcoredump"); + EXPECT_EQ(2, GetNumFiles(temp_dir_generator_.path(), + kDevCoredumpFilePattern)); } // TODO(sque, crosbug.com/32238) - test wildcard cases, multiple identical udev From 92c0eefe42963ab8ea57ad97483d484d4e6137b8 Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Thu, 2 Apr 2015 14:31:10 -0700 Subject: [PATCH 221/225] platform2: Explicitly use base:: namespace with CommandLine The current revision of libchrome is removing CommandLine class from global namespace and only retain the one in base::. Sweep the code to use the correct namespace in anticpation of libchrome revision roll. BUG=None TEST=./build_packages Change-Id: Ib7ca448dac1204c1e36ab053e775a5a5214997ac Reviewed-on: https://chromium-review.googlesource.com/263785 Trybot-Ready: Alex Vakulenko Tested-by: Alex Vakulenko Reviewed-by: Alex Deymo Commit-Queue: Alex Vakulenko --- crash_reporter/list_proxies.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc index de02f0ac4..de6ef0a61 100644 --- a/crash_reporter/list_proxies.cc +++ b/crash_reporter/list_proxies.cc @@ -245,8 +245,8 @@ static bool ShowBrowserProxies(std::string url, base::TimeDelta timeout) { } // namespace int main(int argc, char *argv[]) { - CommandLine::Init(argc, argv); - CommandLine* cl = CommandLine::ForCurrentProcess(); + base::CommandLine::Init(argc, argv); + base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); if (cl->HasSwitch(kHelp)) { LOG(INFO) << kHelpMessage; @@ -273,7 +273,7 @@ int main(int argc, char *argv[]) { chromeos::InitLog(init_flags); std::string url; - CommandLine::StringVector urls = cl->GetArgs(); + base::CommandLine::StringVector urls = cl->GetArgs(); if (!urls.empty()) { url = urls[0]; LOG(INFO) << "Resolving proxies for URL: " << url; From f17a8387d4a2e36523da931cf39de2a7a466c300 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Fri, 24 Apr 2015 14:52:45 -0700 Subject: [PATCH 222/225] crash: update OWNERS file BUG=None TEST=None Change-Id: I5a43ab1aa1eda040ed58aea4f593ccca9bba128d Reviewed-on: https://chromium-review.googlesource.com/267127 Commit-Queue: Ben Chan Tested-by: Ben Chan Reviewed-by: Mike Frysinger --- crash_reporter/OWNERS | 1 - 1 file changed, 1 deletion(-) diff --git a/crash_reporter/OWNERS b/crash_reporter/OWNERS index 5f9a4baef..96ea5b232 100644 --- a/crash_reporter/OWNERS +++ b/crash_reporter/OWNERS @@ -1,3 +1,2 @@ set noparent -benchan@chromium.org vapier@chromium.org From f3caa5a236d5aa99d77860e6a8bbbf54fe8d4820 Mon Sep 17 00:00:00 2001 From: Chris Masone Date: Tue, 28 Apr 2015 12:52:51 -0700 Subject: [PATCH 223/225] crash: Point to canonical org.chromium.SessionManagerInterface.xml The session_manager already had a fully-specified .xml file describing its DBus API, so toss the one created for use by crash-reporter.gyp, and point the build to the canonical one. BUG=None TEST=build crash-reporter and run unit tests Change-Id: Id14e1a5a3a2aa7be3caee309c04d85078e43a315 Reviewed-on: https://chromium-review.googlesource.com/267726 Trybot-Ready: Chris Masone Tested-by: Chris Masone Reviewed-by: Steve Fung Commit-Queue: Chris Masone --- crash_reporter/crash-reporter.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crash_reporter/crash-reporter.gyp b/crash_reporter/crash-reporter.gyp index 988cb5d4d..a7f0e7ecc 100644 --- a/crash_reporter/crash-reporter.gyp +++ b/crash_reporter/crash-reporter.gyp @@ -43,7 +43,7 @@ 'proxy_output_file': 'include/session_manager/dbus-proxies.h' }, 'sources': [ - '../login_manager/dbus_bindings/org.chromium.SessionManager.xml', + '../login_manager/org.chromium.SessionManagerInterface.xml', ], 'includes': ['../common-mk/generate-dbus-proxies.gypi'], }, From 8a59e11e339603d8c4acd2269e65e60afac688be Mon Sep 17 00:00:00 2001 From: Dan Colish Date: Wed, 3 Jun 2015 15:11:21 -0700 Subject: [PATCH 224/225] crash: do not call list-proxies if it does not exist. Additionally, this adds logging for list-proxies call failures. BUG=chromium:396033 TEST=unittests, test_that logging_UserCrash CQ-DEPEND=CL:275076 Change-Id: I0515e75476bdc7a0ace8ebc8318e82c337a87374 Reviewed-on: https://chromium-review.googlesource.com/275077 Reviewed-by: Mike Frysinger Commit-Queue: Daniel Colish Tested-by: Daniel Colish --- crash_reporter/crash_sender | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender index 2ad1e4635..641ae2daa 100755 --- a/crash_reporter/crash_sender +++ b/crash_reporter/crash_sender @@ -419,7 +419,18 @@ send_crash() { # double-quotes are necessary due to a bug in dash with the "local" # builtin command and values that have spaces in them (see # "https://bugs.launchpad.net/ubuntu/+source/dash/+bug/139097"). - local proxy="`${LIST_PROXIES} -quiet "${url}" | head -1`" + if [ -f "${LIST_PROXIES}" ]; then + local proxy ret + proxy=$("${LIST_PROXIES}" --quiet "${url}") + ret=$? + if [ ${ret} -ne 0 ]; then + proxy='' + lecho -psyslog.warn \ + "Listing proxies failed with exit code ${ret}" + else + proxy=$(echo "${proxy}" | head -1) + fi + fi # if a direct connection should be used, unset the proxy variable. [ "${proxy}" = "direct://" ] && proxy= local report_id="${TMP_DIR}/report_id" From c686fd3cbd62864f5d98c8fc5be291ad9b656d99 Mon Sep 17 00:00:00 2001 From: Christopher Wiley Date: Wed, 8 Jul 2015 10:30:05 -0700 Subject: [PATCH 225/225] Remove superfluous legacy DBus includes This header pulls in glib dependencies which aren't used in these codebases. BUG=chromium:508218 TEST=trybots Change-Id: Iecf8dfcdd8064b1feb694382eea55c3f0df572d3 Reviewed-on: https://chromium-review.googlesource.com/284053 Tested-by: Christopher Wiley Reviewed-by: Alex Vakulenko Commit-Queue: Christopher Wiley Trybot-Ready: Christopher Wiley --- crash_reporter/chrome_collector.cc | 1 - crash_reporter/crash_collector.cc | 1 - 2 files changed, 2 deletions(-) diff --git a/crash_reporter/chrome_collector.cc b/crash_reporter/chrome_collector.cc index e32ef5670..ec291c00b 100644 --- a/crash_reporter/chrome_collector.cc +++ b/crash_reporter/chrome_collector.cc @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc index f970d1dfe..04f3ba812 100644 --- a/crash_reporter/crash_collector.cc +++ b/crash_reporter/crash_collector.cc @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include